I'm new to javascript and there's a problem I'm not all able to solve.
I have one array of objects, which contains 2 attributes. I want to connect the attributes, one of which will be a key and the other will be a value.
this is what I have:
[{"prefer":"code_html","rating":"5"},{"prefer":"code_css","rating":"3"}]
This is what I want to get:
[
{
"code_html": "5"
},
{
"code_css": "3"
}
]
I run this function:
const array = [{"prefer":"code_html","rating":"5"},{"prefer":"code_css","rating":"3"}]
const result = array.map(({prefer, rating}) => ({[prefer]: rating}));
console.log(result);
But I can not understand why it does not work for me.
This is the print I get, I do not understand what is wrong
[{},{},{}]
I use this code in nodeJs, maybe that's why I have a problem:
exports.addUserKmeansMatchVer2 = (req, res) => {
console.log("addUserKmeansMatch function filter:");
arr = [];
if(req.query.filterArray)
{
arr = [...req.query.filterArray];
console.log("line 256" + typeof(req.query.filterArray));
//this is print line 256object
console.log("line 257" +arr);
//this is works prints: line 257{"prefer":"sport_swimming","rating":"3"},{"prefer":"code_html","rating":"5"},{"prefer":"code_css","rating":"3"}
console.log("line 258" + req.query.filterArray);
//print exactly the same as line 257
}
let onlyPreferencesAllow = [];
arr.forEach(({prefer,rating}) => onlyPreferencesAllow.push({[prefer]: rating}));
console.log("line 262" + JSON.stringify(onlyPreferencesAllow));
//this is NOT work prints: line 262[{},{},{}]
db.doc(`/match/${req.user.handle}`)
.set("testing")
.then(() => {
return res.json({ message: "Details added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
})
})
};
I noticed that in line 257 it prints for me without the parentheses of the array without [], but in line 262 it prints with the parentheses [], I do not quite understand it
I thought of something I forgot to mention,
I get the req.query.filterArray, through the params.
Here's how I do it:
export const makeMatchVer2 = (data) => (dispatch) => {
dispatch({ type: LOADING_DATA });
axios
.get('/kmeansFilter', {
params: {
filterArray: data
}
})
.then((res) => {
dispatch({
type: MAKE_MATCH,
payload: res.data
});
})
.catch((err) => {
dispatch({
type: MAKE_MATCH,
payload: []
});
});
};
the data itself is an array, maybe here i do the mistake
The solution that worked for me:
When I send an array in params to api, I need to use JSON.stringify.
export const makeMatchVer2 = (data) => (dispatch) => {
dispatch({ type: LOADING_DATA });
axios
.get('/kmeansFilter', {
params: {
filterArray: JSON.stringify(data)
}
})
.then((res) => {
dispatch({
type: MAKE_MATCH,
payload: res.data
});
})
.catch((err) => {
dispatch({
type: MAKE_MATCH,
payload: []
});
});
};
And when I get the answer in nodeJS, I have to use JSON.parse
exports.addUserKmeansMatchVer2 = (req, res) => {
console.log("addUserKmeansMatch function filter:");
arr = [];
if(req.query.filterArray)
{
arr = JSON.parse(req.query.filterArray);
}
let onlyPreferencesAllow = [];
arr.forEach(({prefer,rating}) => onlyPreferencesAllow.push({[prefer]: rating}));
console.log("line 262" + JSON.stringify(onlyPreferencesAllow));
db.doc(`/match/${req.user.handle}`)
.set("testing")
.then(() => {
return res.json({ message: "Details added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
})
})
};
I currently have a check I'm running to see if a username is taken or not. I am querying to see if the username has been taken, and if so provide a value to my errors object. I want to pass my errors defined within my if statement to the outer return statement. Is there a way to go about this?? Im unsure of what to do here.
exports.reduceUserDetails = data => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
} else {
console.log('im not taken')
}
})
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false
}
}
here is where I'm using the reduce user details:
exports.profileUpdate = (req, res) => {
let userDetails = req.body
const { valid, errors } = reduceUserDetails(userDetails)
if (!valid) return res.status(400).json(errors)
let document = db
.collection('users')
.where('username', '==', req.user.username)
document
.get()
.then(snapshot => {
snapshot.forEach(doc => {
const data = doc.id
db.collection('users').doc(data).update(req.body)
})
res.json({ message: 'Updated Successfully' })
})
.catch(error => {
console.error(error)
return res.status(400).json({
message: 'Cannot Update the value'
})
})
}
May be abstracting the call in a new function and awaiting it in caller might work, otherwise you will need to add await before reduceUserDetails() wherever you will call
exports.reduceUserDetails = async data => {
let check = await dupChk(data);
return {
check.errors,
valid: check.result
}
}
var dupChk = (data) => (
new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({result:false,errors:errors})
} else {
resolve({result:true, errors: errors});//console.log('im not taken')
}
})
})
);
UPDATE:
Ok no need to do the above stuff just change the reduceUserDetails() like this
exports.reduceUserDetails = data => {
return new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({valid:false,errors:errors})
} else {
resolve({valid:true, errors: errors});//console.log('im not taken')
}
})
.catch(()=>resolve({result:false,errors:errors}))
})
}
And in profileUpdate() add await keyword before the reduceUserDetails() call
const { valid, errors } = await reduceUserDetails(userDetails)
I have found out that my and my colleagues lambda doesn't return data we anticipate.
After what I have already found, we used deprecated invokeAsync which returnes only statusCode.
I have upgraded aws-sdk to the newest version and change the invocation code to use invoke with
InvocationType: 'RequestResponse'
as that should give us the returned value.
Lambdas code itself looks good, its an async function without arguments.
Im not using the callback here (3rd argument of handler), as we are doing some async stuff and it would require a small refactor to not use async/await, also i have read that just returning value is ok as well.
Earlier lambda was returning array, but after some investigation and googling I have changed it to
return {
statusCode: 200,
body: JSON.stringify(result)
}
where result is the mentioned array, as this was recurring pattern in search results.
Overall result is in our service where we do the invocation, we get timeout, lambda logs that it is returning value, but then retries itself again and again.
Dumping code
invocation in our service:
const lambda = new AWS.Lambda({ httpOptions: { timeout: 600000 } })
const results = await new Promise((resolve, reject) => {
lambda.invoke(
{
FunctionName: `lambda-name`,
InvocationType: 'RequestResponse',
Payload: '""'
},
(err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
}
)
})
lambda handler
import { parallelLimit } from 'async'
const PARALLEL_FETCHERS_NUMBER = 2
export async function runTasksInParallel (
tasks,
limit,
) {
return new Promise(
(
resolve,
reject,
) => {
parallelLimit(tasks, limit, (error, results) => {
if (error === null) {
resolve(results)
} else {
reject(error)
}
})
},
)
}
async function connectToDB(logger) {
const MONGO_URL = process.env.MONGO_URL || 'mongodb://localhost:27017/'
let db
logger.debug('Connecting to mongo')
try {
db = await MongoClient.connect(
MONGO_URL,
{
sslValidate: false,
}
)
} catch (error) {
logger.error('Could not connect to Mongo', { error })
throw error
}
logger.debug('Connected')
return db
}
async function fetchAndStore(
list,
sources,
logger
) {
const results = []
const tasks = sources.map((source) => async (callback) => {
const { fetchAdapter, storageAdapter } = source
try {
const { entries, timestamp } = await fetchAdapter.fetchLatest(list)
await storageAdapter.storeMany(timestamp, entries)
results.push(['fetch.success', 1, [ `fetcher:${fetchAdapter.name}` ] ])
} catch (error) {
const errorMessage = `Failed to fetch and store from adapter ${fetchAdapter.name}`
const { message, name, stack } = error
logger.error(errorMessage, { error: { message, name, stack } })
results.push(['fetch.error', 1, [ `fetcher:${fetchAdapter.name}` ] ])
}
callback && callback(null)
})
await runTasksInParallel(tasks, PARALLEL_FETCHERS_NUMBER)
return results
}
export async function handle() {
const logger = createLambdaLogger() // just a wrapper for console.log to match interface in service
logger.debug('Starting fetching data')
const db: Db = await connectToDB(logger)
const primaryCollection = db.collection(PRIMARY_COLLECTION)
const itemsColletion = db.collection(ITEMS_COLLECTION)
const secondaryCollection = db.collection(SECONDARY_PRICING_COLLECTION)
const primaryStorageAdapter = new StorageAdapter(
Promise.resolve(primaryCollection),
logger
)
const itemsStorageAdapter = new ItemsStorageAdapter(
Promise.resolve(itemsColletion),
logger
)
const secondaryStorageAdapter = new StorageAdapter(
Promise.resolve(secondaryCollection),
logger
)
const primaryFetchAdapter = new PrimaryFetchAdapter(getFetcherCredentials('PRIMARY'), logger)
const secondaryFetchAdapter = new SecondaryFetchAdapter(getFetcherCredentials('SECONDARY'), logger)
const sources = [
{ fetchAdapter: primaryFetchAdapter, storageAdapter: primaryStorageAdapter },
{ fetchAdapter: secondaryFetchAdapter, storageAdapter: secondaryStorageAdapter },
]
try {
const list = await itemsStorageAdapter.getItems()
logger.debug(`Total items to fetch ${list.length}`)
const result = await fetchAndStore(list, sources, logger)
logger.debug('Returning: ', { result })
return {
statusCode: 200,
body: JSON.stringify(result)
}
} catch (error) {
const errorMessage = 'failed to do task'
const { message, name, stack } = error
logger.error(errorMessage, { error: { message, name, stack } })
return {
statusCode: 500,
body: JSON.stringify(new Error(errorMessage))
}
} finally {
await db.close()
}
}
Edit:
I have changed the way i'm invoking the lambda to this below. Basically I tried to listen for every possible event that can say me something. Result is that i get a error event, with response message being Converting circular structure to JSON. Thing is I don't really know about which structure this is, as the value I'm returning is a two elements array of 3 elements (strings and number) arrays.
const lambda = new AWS.Lambda({
httpOptions: { timeout: 660000 },
maxRetries: 0
})
const request = lambda.invoke(
{
FunctionName: `${config.get('env')}-credit-rubric-pricing-fetching-lambda`,
InvocationType: 'RequestResponse',
Payload: '""'
},
(err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
}
)
const results = await request
.on('send', () => {
logger.debug('Request sent to lambda')
})
.on('retry', response => {
logger.debug('Retrying.', { response })
})
.on('extractError', response => {
logger.debug('ExtractError', { response })
})
.on('extractData', response => {
logger.debug('ExtractData', { response })
})
.on('error', response => {
logger.debug('error', { response })
})
.on('succes', response => {
logger.debug('success', { response })
})
.on('complete', response => {
logger.debug('complete', { response })
})
.on('httpHeaders', (statusCode, headers, response, statusMessage) => {
logger.debug('httpHeaders', { statusCode, headers, response, statusMessage })
})
.on('httpData', (chunk, response) => {
logger.debug('httpData', { response, chunk })
})
.on('httpError', (error, response) => {
logger.debug('httpError', { response, error })
})
.on('httpDone', response => {
logger.debug('httpDone', { response })
})
.promise()
I have action that always returns Promise.reject:
module.exports = { create: createActionAsync('CREATE_USER', () => {
return Promise.reject({
response: {
type: 'error',
message: 'It will be implemented soon',
},
});
})}
But in component catch block doesn't work:
onAddUser(data) {
const { userActions: { create } = {} } = this.props;
create(data)
.then(() => {})
.catch(err => console.error(err)) // not working
I want to reward a user when he undertakes an action. It can happen the path to his 'coins' does not exists yet. That is why I get the error:
Transaction failure: Error: The data for XXX does not exist.
How can I run a transaction while the path can not exist yet? This is what I tried:
exports.facebookShared = functions.firestore.document('facebookShared/{randomUID}').onCreate(event => {
const data = event.data.data()
const uid = data.uid
var promises = []
promises.push(
db.collection('facebookShared').doc(event.data.id).delete()
)
const pathToCoins = db.collection('users').doc(uid).collection('server').doc('server')
promises.push(
db.runTransaction(t => {
return t.get(pathToCoins)
.then(doc => {
var newCoins = 0
if (doc.data().hasOwnProperty("coins")){
newCoins = doc.data().coins + awardFacebookShare.coins
}
t.update(pathToCoins, { coins: newCoins });
});
})
.then(result => {
console.log('Transaction success', result);
})
.catch(err => {
console.log('Transaction failure:', err);
})
)
return Promise.all(promises)
})
I came across this docs: https://cloud.google.com/nodejs/docs/reference/firestore/0.8.x/Firestore#runTransaction
That docs are better than here: https://firebase.google.com/docs/firestore/manage-data/transactions
Below code works:
exports.facebookShared = functions.firestore.document('facebookShared/{randomUID}').onCreate(event => {
const data = event.data.data()
const uid = data.uid
var promises = []
promises.push(
db.collection('facebookShared').doc(event.data.id).delete()
)
const pathToCoins = db.collection('users').doc(uid).collection('server').doc('server')
promises.push(
db.runTransaction(t => {
return t.get(pathToCoins)
.then(doc => {
var newCoins = awardFacebookShare.coins
if (doc.exists){
if (doc.data().hasOwnProperty("coins")){
newCoins += doc.data().coins
}
t.update(pathToCoins, { coins: newCoins });
return Promise.resolve(newCoins);
}else{
t.create(pathToCoins, { coins: newCoins });
return Promise.resolve(newCoins);
}
});
})
.then(result => {
console.log('Transaction success', result);
})
.catch(err => {
console.log('Transaction failure:', err);
})
)
return Promise.all(promises)
})