I have a lambda which sits on the business layer (GoalFeed), it's function is to aggregate data from two other lamdbas (Goals and Users).
The GoalFeed invokes Goals (RESTful/GET) and iterates over the results, something of this nature (excuse missing code for brevity):
lambda.invoke( goalsParms, function( err, data ) {
var items = data.Payload.body.Items;
items.forEach( function( element ) {
lambda.invoke( teamsParms, function( err, data ) {
// PROBLEM: element is always the *last* element here!!
});
});
});
So the issue I'm having is that I'd like to pass (reference) each element in the nested lambda.invoke callback, but I don't see a way to make that happen. Referencing element in the lambda.invoke (teams) call always gives me the last element in the list.
How do I go about passing the element/or properly referencing it inside the callback for the nested lambda.invoke (teams), so that when the nested invoke executes it will fetch data for the current element during the initial invoke?
I believe this was something to do with how I was handling promises further up the chain, though creating a separate function and calling it with element seems to have resolved the problem.
Related
I have this code snippet:
const historicalDataTable = await findElementByClass(
"table",
elementClass,
page
); // This is a custom function I wrote. Works as expected.
const tableBody = await historicalDataTable.$eval(
"tbody",
(el) => el.children.length
);
console.log(tableBody);
This works as expected and returns the correct amount of children. However when I do
const tableBody = await historicalDataTable.$eval(
"tbody",
(el) => el.children
);
And remove the length, it returns undefined. What is going on here?
el.children (Element#children) will yield an HTMLCollection which is not serializable and can't be marshalled from the page's execution context into yours, so evaluate returns undefined instead.
Now, this isn't fully obvious when looking at the elementHandle.$eval docs as the only indication is that the return value is <Promise<Serializable>>, but it becomes clear from the executionContext.evaluate docs:
returns: <Promise<Serializable>> Promise which resolves to the return value of pageFunction
[...]
If the function passed to the executionContext.evaluate returns a non-Serializable value, then executionContext.evaluate resolves to undefined. DevTools Protocol also supports transferring some additional values that are not serializable by JSON: -0, NaN, Infinity, -Infinity, and bigint literals.
(Emphasis mine.)
el.children.length (HTMLCollection#length) on the other hand is a simple number which is serializable.
You have to do whatever you want to do with those elements inside of your pageFunction and return only some serializable value.
Alternatively, you could also use elementHandle.evaluateHandle to return a JSHandle to the HTMLCollection and use that handle later in another call to an evaluate-type function. (Note that that would be the only thing you can do with it though. You couldn't access .length for example from your own execution context, only from another pageFunction1.)
1: This is not entirely true, since you could for example use jsHandle.getProperty to get another JSHandle for the length, followed by jsHandle.jsonValue to get the value as number - but both of these operations are asynchronous and probably it's a lot more efficient to write your code in such a way that you can handle all the necessary operations inside the page's execution context in the first place, without too many context switches.
This question already has answers here:
mongoose .find() method returns object with unwanted properties
(5 answers)
Closed 5 years ago.
Working with a strange problem here. This is an array of objects which is pulled from mongodb and passed into the following function.
I tried the following 3 logs sequentially within the forEach on the array pulled from the database:
e (the object element within the array) which returns correctly. as you can see all the properties (keys) exist:
{ paid: false,
hotelWebsite: 'www.testing.com',
_id:5951848a24bb261eed09d638,
hotelAddress: '123 easy street',
...etc }
console.log(Object.keys(e)) is returning things that are not the keys...
[ '__parentArray',
'__parent',
'__index',
'$__',
'isNew',
'errors',
'_doc',
'$init' ]
and finally:
for(key in e){
console.log(key);
}
which returns an absolute mess of data, part of which DOES contain the actual keys of the object:
__parentArray
__parent
__index
$__
isNew
errors
_doc
$init
id
_id
hotelWebsite
hotelAddress
hotelNumber
hotelName
courseCost
courseDate
courseState
courseCity
courseName
paid
studentComments
studentEmail
studentPhone
studentCountry
studentZip
studentState
studentCity
studentAddress
studentCompany
studentName
schema
constructor
$__original_remove
remove
_pres
_posts
$__original_validate
validate
toBSON
markModified
populate
save
update
inspect
invalidate
$markValid
$isValid
ownerDocument
$__fullPath
parent
parentArray
on
once
emit
listeners
removeListener
setMaxListeners
removeAllListeners
addListener
$__buildDoc
init
$hook
$pre
$post
removePre
removePost
_lazySetupHooks
set
$__shouldModify
$__set
getValue
setValue
get
$__path
unmarkModified
$ignore
modifiedPaths
isModified
$isDefault
isDirectModified
isInit
isSelected
isDirectSelected
$__validate
validateSync
$__reset
$__dirty
$__setSchema
$__getArrayPathsToValidate
$__getAllSubdocs
$__handleReject
$toObject
toObject
toJSON
toString
equals
execPopulate
populated
depopulate
And a relevant sample of the code if needed:
studentsArray.forEach( (e, i) => {
if(task === 'nameTag'){
console.log(e);
console.log(Object.keys(e));
for(k in e){
console.log(k);
}
}
....
I need access to the properties (keys) for further processing within the forEach function. I am very confused on what is causing this and have never run into this sort of issue before. For the record the objects exist, using a console.log(typeof e) it IS an object (not a data "string"). I can access the properties using the dot or bracket notation but NOT using Object.keys() or for (keys in obj).
Can anyone help me sort this out please?
for ... in iterates all enumerable properties, both own and inherited. This is not "a strange bug," this is in fact the intended behavior.
As for the Object.keys(), unless it was overwritten by a non-compliant implementation, those are in fact enumerable keys of the object itself, so you are most likely mistaken. The e object has a .toJSON() method in its prototype that is implicitly called when you do console.log(e), so that is probably the output you are seeing there, and is not likely going to reflect exactly the same property keys as the original object. Try calling console.log(e.toJSON()) and I'm guessing it will be the same output as in the first one.
If you want only the object's own properties, use Object.getOwnPropertyNames(e).
If you want the keys printed in the first output, then use Object.keys(e.toJSON()).
I am trying to check for the existence of an optional field in an API request, and if that field exists, perform a nested validation to check if two other fields (one or the other, or implicitly both) exist inside of it. I am using Express Validator to try and accomplish this task.
// Sample request body
{
<...>
thresholds: {
min: 3,
max: 5
}
}
// (Attempted) validation chain
check('thresholds').optional()
.custom( innerBody => {
console.log('THRESHOLDS', innerBody);
oneOf([
check('innerBody.min').optional(),
check('innerBody.max').optional()
]);
})
The above snippet is part of a larger validation chain I'm validating the full request body on. I also tried removing the innerBody. string from the inner checks but still no luck. I am console.loging the threshold body, and it prints out correctly, however I still get a validation error, when I'm trying to get my integration test to pass:
{"name":"ValidationError","message":"ValidationError: Validation failed","errors":[{"location":"body","param":"thresholds","value":{"min":3,"max":5},"msg":"Invalid value"}]}
I am relatively new to Express Validator so if I'm chaining the validation wrong/not using oneOf correctly or something would love some pointers!
Thanks
Looks like the .custom function needs to return a Promise. Answer below:
.custom(innerBody => {
if (!(innerBody.min) || !(innerBody.max)) return Promise.reject('Missing min or max');
return Promise.resolve();
})
Remember: Always return a boolean value from the callback of .custom()
function. Otherwise your validation might not work as desired.
Source: Custom validation guide
In general, you might have needs in use of Promises if you deal with asynchronous .custom() function. Then you'll be obligated to return Promise.resolve() / Promise.reject() for correct validator behaviour.
Source: SO answer
In the feathersjs docs the explanation provided is as follows:
pluck discards all fields except for the specified ones, either from
the data submitted or from the result. If the data is an array or a
paginated find result the hook will remove the field(s) for every
item.
import _pluck from '../common/_pluck';
import checkContextIf from './check-context-if';
import getItems from './get-items';
import replaceItems from './replace-items';
export default function (...fieldNames) {
return context => {
checkContextIf(context, 'before', ['create', 'update', 'patch'], 'pluck');
if(context.params.provider) {
replaceItems(context, _pluck(getItems(context), fieldNames));
}
return context;
};
}
The getItems utility returns the items in either hook.data or
hook.result depending on whether the hook is being used as a before or
after hook. hook.result.data or hook.result is returned for a find
method.
The returned items are always an array to simplify further processing.
The replaceItems utility is the reverse of getItems , returning the
items where they came from.
My question relates to the checkContextIf function. This function prevents the pluck hook from being called except before the create,update and patch methods. How then does the pluck hook work on the results of the query. Are not the results produced after the service call and handled in an after hook?
As the documentation states:
The getItems utility returns the items in either hook.data or hook.result depending on whether the hook is being used as a before or after hook.
hook.data is the data (body) sent with a create, patch or update request so it can be used to omit fields that you do not want to be saved to the database. This is also documented in the hooks API:
data - The request data (for create, update and patch)
For testing we have to fill a complex page using intern/leadfoot. Each part of the page is taken care off by a separate function which receives the necessary elements and input data.
Now we have the problem, that actions on these elements in the subfunctions can not be chained anymore, since they are elements and not commands.
Is it somehow possible to again chain the operations? I tried a lot with creating a new command using setContext() or with custom commands but did not succeed so far.
let inputs;
return this.remote
.get('some/url')
.findAllByTagName('input') // Finds two input elements
.then(inputElements=> inputs = inputElements)
.then(()=> Promise.all([
inputs[0].clearValue(), // I would like to be able to write: inputs[0].clearValue().type('a')
inputs[1].clearValue(),
]))
.then(()=> Promise.all([
inputs[0].type('a'),
inputs[1].type('b'),
]))
Elements share many of the same methods as Commands, but they have different APIs. A primary difference is that Command methods representing actions return Commands (a Command is promise-like, and resolves when the action finishes), but Element methods representing actions do not return Elements (an Element is not promise-like). This means that you can't directly chain many Element methods.
For the situation described in the question, you could do something like the following:
function clearAndType(input, value) {
return remote.then(function (_, setContext) {
// setContext is always the last argument to a Command then()
// callback; the value returned by the previous Command is the
// first argument, which is ignored here
setContext(input);
})
.clearValue()
.type(value);
}
var remote = this.remote;
return this.remote
.get('some/url')
.findAllByTagName('input')
.then(function (inputs) {
return Promise.all([
clearAndType(inputs[0], 'a'),
clearAndType(inputs[1], 'b'),
// ...
]);
})