Passing file as base64 to Primefaces' remote command - jsf

I have an array of File objects on my client side and I'm trying to send b64 value of that files to managed bean.
HTML:
<p:remoteCommand name="convertToPdfa" actionListener="#{bean.convertFile}" />
Bean method:
public String convertFile() {
String index = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("index"); // not empty
String data = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("data"); // not empty
String retry = Boolean.parseBoolean(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("retry")); // empty
return "";
}
Javascript:
convertAll: async function (noOfFiles) {
for (let i = 0; i < noOfFiles; i++) {
await sendFileDataAndConvert(i);
}
},
sendFileDataAndConvert: async function (index) {
let promise = new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(getFile(index));
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
// wait until the promise returns us a value
let result = await promise;
// "Now it's done!"
console.log(result); // result is not empty, file is converted
convertToPdfa([{name: 'index', value: index}, {name: 'data', result}, {name: 'retry', value: false}]);
}
Now since I'm using async function to convert File to base64, I need to wait for the result to be available before I invoke remote command. I'm guessing that parameters passed to remote command are set before the result is available? Is remote command invoked before result is available? Because when I try to read parameters in managed bean method, data is empty, but the other two parameters are not empty. I don't exactly understand what's going on here. I'm trying to wait for the result using async/await mechanism, but it looks like I'm missing something.
How can I achieve that remote command parameters are set after async function is done?

Just found out it was a typo. Instead of
convertToPdfa([{name: 'index', value: index}, {name: 'data', result}, {name: 'retry', value: false}]);
it should be
convertToPdfa([{name: 'index', value: index}, {name: 'data', value: result}, {name: 'retry', value: false}]);

Related

Query a DynamoDB table while passing a parameter nested within a forEach() method of an array

I'm scanning all items from a DynamoDB table - within a Lambda function - with DocumentClient. I'm then looping through each item and extracting the payload that I need. I'll use that item from the payload as a parameter with ExpressionAttributeValues in a new query.
Everything works dandy independently. The issue is with the use of the asynchronous function queryItems when nested within an array forEach() method. I getting a parsing error with the function queryItems. I can query the table when I call the function outside of the loop but how else am I going to query each item independently?
I'm not sure how to handle this.
'use strict';
const aws = require('aws-sdk');
const docClient = new aws.DynamoDB.DocumentClient();
var paramsAll = {
TableName: 'MyTable',
Select: "ALL_ATTRIBUTES"
};
exports.handler = async (event, context) => {
try {
let arr = [];
let sequence = '';
//scan all items in table
docClient.scan(paramsAll, function(err, data) {
if (err) {
//handle error
}
else {
//Loop through each item in the table:
let items = (data.Items);
items.forEach(function(Item) {
let p = (Item.payload);
//Extract sequence from the payload
sequence = (p.seq);
arr.push(sequence);
//perform other function with this array (not listed for brevity)
});
//Here is where I'm having the issue:
arr.forEach(function(Item) {
//Pass these items as a paramater within queryItems function but getting Parsing Error: unexpected token queryItems
const results = await queryItems(Item);
//do something with the results...
})
}
});
}
catch (err) {
return { error: err };
}
};
async function queryItems(p) {
try {
var params = {
TableName: 'MyTable',
KeyConditionExpression: '#seq = :value',
ExpressionAttributeValues: { ':value': p },
ExpressionAttributeNames: { '#seq': 'seq' }
};
const data = await docClient.query(params).promise();
return data;
}
catch (err) {
return err;
}
}
I've definitely run into a similar issue. What I believe is happening is just a Javascript syntax issue, where awaiting queryItems inside the synchronous function provided to forEach will produce an error. (Although, when running the code, I do get the specific error "SyntaxError: await is only valid in async functions and the top level bodies of modules", so there might be something else going on.)
I see nothing wrong with the DynamoDB queries, but hoangdv's suggestions are spot on. Specifically, I'd also suggest using the promise style for scan, and while a for...loop will definitely work, using Promise.all and map will be a lot quicker to complete all the queries. Here's how I'd modify the code:
'use strict';
const aws = require('aws-sdk');
const docClient = new aws.DynamoDB.DocumentClient();
// avoid var unless you specifically require it's hoisting behavior.
const paramsAll = {
TableName: 'MyTable',
Select: "ALL_ATTRIBUTES" // most likely not needed, I'd review this section of the docs: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-Select
};
exports.handler = async (event, context) => {
try {
// unless you need to assign a new array to this variable, it is better practice to use const instead.
const arr = [];
// let sequence = ''; // see line 24 for why I commented this out.
// scan all items in table.
// Destructure Items out of the response.
// You may also need to continue scanning with the LastEvaluatedKey depending on the size of your table, and/or your use case.
// You'd continue scanning in a while loop, for example.
const { Items, LastEvaluatedKey } = await docClient.scan(paramsAll).promise();
// push the sequence to the arr.
// There is most likely a reason you omitted for brevity to have sequence defined above,
// but since this example doesn't need it above, I've omitted it entirely
Items.forEach(Item => {
const p = Item.payload;
arr.push(p.seq);
});
// use a for loop or map here instead. forEach will return undefined, which cannot be await'ed.
// instead, map will return a new array of Promises (since the callback is async).
// Then, you can use Promise.all to await until each Promise in the array is resolved.
// Keep in mind, depending on how many items you are iterating through, you may run into DynamoDB's ThrottlingException.
// You would have to batch the queries (in other words, split the arr into pieces, and iterate over each piece), which would have to be done before using map. Then, sleep for a few milliseconds before starting on the next piece.
// I doubt the queries will be quick enough to cause this when using a for loop, though.
await Promise.all(arr.map(async Item => {
const results = await queryItems(Item);
// do something with the results...
}));
}
catch (err) {
// Again, not sure what the use case is, but just FYI this is not a valid return value if this lambda function is intended for use with using API Gateway.
// See here :) https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#apigateway-types-transforms
return { error: err };
}
};
// Presumably, MyTable has a partitionKey of seq, otherwise this KeyConditionExpression is invalid.
async function queryItems(p) {
try {
var params = {
TableName: 'MyTable',
KeyConditionExpression: '#seq = :value',
ExpressionAttributeValues: { ':value': p },
ExpressionAttributeNames: { '#seq': 'seq' }
};
const data = await docClient.query(params).promise();
return data;
}
catch (err) {
return err;
}
}
Your issue is how you await on the for loop, its best to use Promise.all() with a map to await inside of a loop:
await Promise.all(arr.map(async Item => {
const results = await queryItems(Item);
// do something with the results...
}));
However, I cannot seem to understand your logic really well.
You Scan a table called MyTable, but you do not paginate, meaning you are only getting up to 1MB worth of data.
With the results, you strip out the seq value and then once again read every item from MyTable this time using a Query and seq as the key?

How to dynamically generate TestCafe test script where number of testcases would be equal to number items returned by a service call

import { Selector } from 'testcafe';
let appList;
let username = '';
let password = '';
// Start - service call to get JSON Data in appList variable
var requestNode = require('request');
var options = {
'method': 'GET',
'url': 'https://********.com/_layouts/15/cors/testcafeapps.ashx',
'headers': {
'Authorization': '*****w=='
}
};
requestNode(options, function (error, response) {
if (error) throw new Error(error);
appList = JSON.parse(response.body);
});
// End -service call to get JSON Data in appList variable
fixtureValidate
.httpAuth({
username: username,
password: password
})
// iterate through JSON items and create tests for each JSON dataitems
for (const app in appList) {
test('SSO test for ' + appList[app], async t => {
console.log('Checking login for ' + appList[app])
await t
.navigateTo(appList[app])
});
}
I have written above code where I am trying to perform some test steps for each of the app returned by service call. I get an error as "Source file do not contain valid 'fixture' and 'test' declaration"
Instead of using service call if I use hardcoded JSON and iterate through JSON items then my code works but my actual requirement is to not use hardcoded JSON data instead get data from a Service call. Kindly let me know how I can achieve this.
This error appears when a file doesn't contain any tests and this is your case. request is an asynchronous function and you don't wait until it is finished and try to run tests for appList but appList is empty in that moment. To run tests only after executing request, you can promisify your request and use await to wait for the results. But instead of promisifying request, I recommend using axios to execute API requests.
//runner.js
function DoRequest() {
return new Promise(function (resolve) {
setTimeout(() => {
resolve([
'test1',
'test2',
])
}, 2000)
});
}
(async () => {
global.appList = await DoRequest();
const createTestCafe = require('testcafe');
const testcafe = await createTestCafe();
await testcafe
.createRunner()
.src('test.js')
.browsers('chrome')
.run();
await testcafe.close();
})();
//test.js
fixture('Getting Started')
.page('https://devexpress.github.io/testcafe/example');
for (const app in global.appList) {
test('SSO test for ' + app, async t => {
await t
.typeText('#developer-name', 'John Smith')
.click('#submit-button');
});
}

return response data from async call

I created this function to get list all my drives from GDrive.
async getAllDrives(token) {
let nextPageToken = ""
let resultArray = []
const config= {
headers: {
Authorization: `Bearer ${token}`
}
};
const bodyParams = {
pageSize: 2,
fields: 'nextPageToken, drives(id, name)',
q:`hidden=false`,
};
do {
axios.get(
`https://www.googleapis.com/drive/v3/drives`,
config,
bodyParams,
).then(result => {
nextPageToken = result.data.nextPageToken;
resultArray.push(result.data.drives);
resultArray = resultArray.flat();
console.log("result", resultArray);
}).catch(error => {
console.log(error);
//res.send(error);
});
}while(nextPageToken);
resultArray = resultArray.flat();
resultArray.map(drive => {
drive.isSharedDrive = true;
return drive;
});
return JSON.stringify(resultArray);
}
When I look in console.log
then(result => {
nextPageToken = result.data.nextPageToken;
resultArray.push(result.data.drives);
resultArray = resultArray.flat();
console.log("result", resultArray);
})
I have the expected result,
result [
{
kind: 'drive#drive',
id: '**',
name: ' ★ 🌩'
},
]
but return JSON.stringify(resultArray); is empty.
I found a similar question here, How do I return the response from an asynchronous call? but the answer is not satisfying.
You used the async call slightly incorrectly. You calling axios.get without await keyword, but with .then chaining. Since you don't wait for result to return, you getting empty array first, returning you nothing. And only then your callback function inside .then is getting called. To simplify, you doing this in your example:
function getAllDrives() {
// Local variable where you want your result
let result = [];
// You calling the axios.get method, but don't wait for result
axios.get().then(result => {})
// Empty result is getting returned immediately
return result;
}
And when response is returned from the remote server, function inside .then trying to save result to local variable. But function is already completed, so you don't get anything.
What you actually should do is call axios.get with await keyword:
// You should always cover your asynchronous code with a try/catch block
try {
// Instead of `then` callback use `await` keyword. Promise returned from
// this method will contain result. If error occurs, it will be thrown,
// and you can catch it inside `catch`.
const result = await axios.get(
`https://www.googleapis.com/drive/v3/drives`,
config,
bodyParams
);
// Here is your code as you wrote it inside `then` callback
nextPageToken = result.data.nextPageToken;
resultArray.push(result.data.drives);
resultArray = resultArray.flat();
console.log("result", resultArray);
} catch (error) {
// And here is your error handling code as you wrote it inside `catch`
console.log(error);
}
This way your method will not complete until your request is not executed.
You can read more about async/await functions here.
I believe your goal is as follows.
You want to retrieve the drive list using axios.
Your access token can be used for retrieving the drive list using Drive API.
Modification points:
In order to use nextPageToken in the request, in this case, it is required to run the script with a synchronous process. So, async/await is used. This has already been mentioned in the existing answers.
When I saw your script, I thought that the query parameter might be required to be included in the 2nd argument of axios.get().
In order to use nextPageToken, it is required to include the property of pageToken. In your script, pageToken is not used. By this, the infinite loop occurs because nextPageToken is continued to be returned.
When these points are reflected in your script, how about the following modification?
Modified script:
let resultArray = [];
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
params: {
pageSize: 2,
fields: "nextPageToken, drives(id, name)",
q: `hidden=false`,
pageToken: "",
},
};
do {
const { data } = await axios
.get(`https://www.googleapis.com/drive/v3/drives`, config)
.catch((error) => {
if (error.response) {
console.log(error.response.status);
console.log(error.response.data);
}
});
if (data.drives.length > 0) {
resultArray = [...resultArray, ...data.drives];
}
nextPageToken = data.nextPageToken;
config.params.pageToken = nextPageToken;
} while (nextPageToken);
resultArray.map((drive) => {
drive.isSharedDrive = true;
return drive;
});
return JSON.stringify(resultArray);
Testing:
When this script is run, the following result is obtained.
[
{"id":"###","name":"###","isSharedDrive":true},
{"id":"###","name":"###","isSharedDrive":true},
,
,
,
]
Note:
From the official document of "Drives: list",
pageSize: Maximum number of shared drives to return per page. Acceptable values are 1 to 100, inclusive. (Default: 10)
So, when pageSize is 100, the number of loops can be reduced. If you want to test the loop using nextPageToken, please reduce the value.
References:
axios
Drives: list
I recommend you study a little more about async/await.
It makes no sense for you to use async and put a .then().catch(), the purpose of async to get these encapsulated syntaxes.
async getAllDrives(token) {
try {
const getDrives = await this.request(token)
console.log(getDrives)
const results = this.resultArray(getDrives)
return results
} catch (e) {
console.log(e)
}
}
I didn't quite understand your while or your objective, adapt it to your code or remove it
async request(token) {
let nextPageToken = 1 // ????????
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
const bodyParams = {
pageSize: 2,
fields: 'nextPageToken, drives(id, name)',
q: `hidden=false`,
};
let getDrives = [];
// loop for each request and create a request array
for (let x = 0; x < fields.nextPageToken; x++) {
const request = axios.get(
`https://www.googleapis.com/drive/v3/drives`,
config,
bodyParams
);
getDrives.push(request)
}
const drives = await Promise.all(getDrives)
return drives
}
async resultArray(drivers) {
// result treatment here
}
The return of promise all will be an array of the driver's responses
Note: The response in request.data
const request = await axios.get()
const resposta = request.data
Read about
https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Graphql-js Custom Directive execute in Pipeline

i try to get my head around about custom directives. I use the graphql from neo4j. But I think it is a general question. I need to do the following:
Query neo4j and construct a string, that serves as an ID. So I use there #cypher directive. This returns a string
then use this ID and request another endpoint use my custom directive. This will return an array of small objects.
Its basically piping one result of a directive to the other. When writing the transform function for the schema to allow my custom directive, the fieldConfig.resolve-Function does not get called, when its an graphql interface or type. If i would set the return type of my test field to String, then the resolve function is executed.
In the resolve function I do my rest request and return the result. Obviously the result is now an array of objects and not a string. Therefore graphql complains about mismatching types.
directive #Rest on FIELD_DEFINITION
type Query {
test: ResultType #Rest #cypher(statement """ some cypher query return string """)
}
type ResultType {
timestamp: Float
unit: String
value: String
}
(schema: GraphQLSchema) =>
mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const dirs = getDirective(schema, fieldConfig, directiveName);
const fieldDirective = dirs?.[0];
if (fieldDirective) {
const resolve = fieldConfig.resolve || defaultFieldResolver;
fieldConfig.resolve = async (source, args, context, info) => {
const result = await resolve(source, args, context, info);
const arr = await getArgumentValues(source, args, context);
if (typeof result === "string") {
try {
const response = await axios.get("http://some-rest-endpoint");
const data= response.data || [];
return data;
} catch (e) {
console.error(e);
return [];
}
}
return [];
};
}
return fieldConfig;
},
});
Is there a solution or a hint to manage what I try to achieve?

return value from nodejs function

Please, help me
I have a script
export function GetKey(inn, res) {
try {
const body = {
7709798583: {
name: 'someName',
key: '123'
},
7718266352: {
name: 'otherName',
key: '123'
}
};
res(body[inn]['key']);
} catch (err) {
res('0000000000000');
}
};
In other file I try to use this function
GetKey(param, (name) => {
console.log(name);
});
It's ok. but I need to return callback to the parametr. How?
var param = GetKey(param, (name) => {
return name;
});
is not correct and return undefined
That's not how callbacks work; although, you can fake that behavior using Promise and async-await syntax.
If you want to write your code like you have it, you'll want to put the rest of your logic in a function and pass it directly to your callback:
var param = ''
var allYourLogic = name => {
// YOUR LOGIC
param = name
}
GetKey(param, allYourLogic);
Or you can simply inline your logic:
GetKey(param, (name) => {
param = name
// YOUR LOGIC
});
Using the Promise syntax, this is how it looks:
new Promise(resolve => {
GetKey(param, resolve)
})
.then(name => {
param = name
// YOUR LOGIC
})
Lastly, using the async-await methodology:
var param = (
await new Promise(resolve => {
GetKey(param, resolve)
})
)
Really though, it seems like you're doing something wonky which is why you're running into this issue in the first place.
Your entire application will act like it's asynchronous as soon as you use a callback because the callback doesn't execute immediately in Node.js's event loop. Instead, the current function you're in will finish executing before the GetKey function calls the callback method.

Resources