Why is variable being calculated twice in an async promise? - javascript

CURRENTLY
I have the following function IN AWS that utilise Textract SDK async functions:
async function textParse(config) {
const AWS = require("aws-sdk");
let textract = new AWS.Textract();
let analysisResults;
console.log("Before Try");
try {
console.log("Start Document Analysis");
analysisResults = await textract
.startDocumentAnalysis(config, function (err, data) {
if (err) {
console.log("There was an error during doc analysis...");
console.log(err, err.stack);
} // an error occurred
else {
console.log("Doc analysis successful!");
console.log(data);
return data;
} // successful response
})
.promise();
} catch (e) {
throw "Doc Analysis Error!" + e;
}
let JobId = analysisResults["JobId"];
let textractResult = await textract
.getDocumentAnalysis({ JobId: JobId })
.promise();
console.log("End!")
return textractResult;
}
ISSUE
The console log is showing that analysisResults is being calculated twice. Note Doc analysis successful! appearing twice in the logs.
INFO Before Try
INFO Start Document Analysis
INFO Doc analysis successful!
INFO { JobId: '3a1e00c9b5ca9123124hhfdfdsdd02f2053c38ec5249e822c9e95f' }
INFO Doc analysis successful!
INFO { JobId: '5ef298d3a9123124hhfdfdsddsdssdsdsdds689580642a6346' }
INFO End!
I've spent a few hours trying to debug this and I suspect it's something to do with how I'm handling promises.
Any ideas on what changes I need to make?

Don't know the textract library, but this is what the OP code appears to be trying to say in a singular (promise, not callback) style.
async function textParse(config) {
const AWS = require("aws-sdk");
let textract = new AWS.Textract();
console.log("Before Try");
try {
console.log("Start Document Analysis");
let analysisResults = await textract
.startDocumentAnalysis(config)
.promise();
console.log("Doc analysis successful!", analysisResults);
let textractResult = await textract
.getDocumentAnalysis({ JobId: analysisResults["JobId"] })
.promise();
console.log("End!", textractResult);
return textractResult;
} catch (err) {
console.log("There was an error during doc analysis...");
console.log(err, err.stack);
}
}
I worry about answering this way, because it doesn't seem to address the symptom described (two success logs), but even if there's another problem in the code, I think this is still a valid rewrite, form-wise.

Related

Firestore offline cache data not fully synced to cloud

I'm doing around 100 writes to Firestore offline. But after connecting to the internet I see some of my data is missing in the cloud. There are no errors in the writing process though.
If I do the same thing online, all the data is synced to the cloud.
Below is the code I'm trying in offline mode. Each function has around 10 iterates. There are 10 functions running one after the other.
const updateCities = async (cities) => {
for (const city of cities) {
try {
const firebaseCity = await firestore().collection('cities').doc(city.id).get();
await firebaseCity.update(city);
} catch (error) {
console.log(error);
}
}
}
const updateUsers = async (users) => {
for (const user of users) {
try {
const firebaseUser = await firestore().collection('users').doc(user.id).get();
await firebaseUser.update(user);
} catch (error) {
console.log(error);
}
}
}
Are there any limitations in the firebase functionalities or I'm doing something wrong?

Reading Parquet objects in AWS S3 from node.js

I need to load and interpret Parquet files from an S3 bucket using node.js. I've already tried parquetjs-lite and other npm libraries I could find, but none of them seems to interpret date-time fields correctly. So I'm trying to AWS's own SDK instead, in the believe that is should be able to deserialize its own Parquet format correctly -- the objects were originally written from SageMaker.
The way to go about it, apparently, is to use the JS version of
https://docs.aws.amazon.com/AmazonS3/latest/API/API_SelectObjectContent.html
but the documentation for that is horrifically out of date (it's referring to the 2006 API, https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#selectObjectContent-property). Likewise, the example they show in their blog post doesn't work either (data.Payload is neither a ReadableStream not iterable).
I've already tried the response in
Javascript - Read parquet data (with snappy compression) from AWS s3 bucket. Neither of them work: the first uses
node-parquet, which doesn't currently compile, and the second uses parquetjs-lite (which doesn't work, see above).
So my question is, how is SelectObjectContent supposed to work nowadays, i.e., using aws-sdk v3?
import { S3Client, ListBucketsCommand, GetObjectCommand,
SelectObjectContentCommand } from "#aws-sdk/client-s3";
const REGION = "us-west-2";
const s3Client = new S3Client({ region: REGION });
const params = {
Bucket: "my-bucket-name",
Key: "mykey",
ExpressionType: 'SQL',
Expression: 'SELECT created_at FROM S3Object',
InputSerialization: {
Parquet: {}
},
OutputSerialization: {
CSV: {}
}
};
const run = async () => {
try {
const data = await s3Client.send(new SelectObjectContentCommand(params));
console.log("Success", data);
const events = data.Payload;
const eventStream = data.Payload;
// Read events as they are available
eventStream.on('data', (event) => { // <--- This fails
if (event.Records) {
// event.Records.Payload is a buffer containing
// a single record, partial records, or multiple records
process.stdout.write(event.Records.Payload.toString());
} else if (event.Stats) {
console.log(`Processed ${event.Stats.Details.BytesProcessed} bytes`);
} else if (event.End) {
console.log('SelectObjectContent completed');
}
});
// Handle errors encountered during the API call
eventStream.on('error', (err) => {
switch (err.name) {
// Check against specific error codes that need custom handling
}
});
eventStream.on('end', () => {
// Finished receiving events from S3
});
} catch (err) {
console.log("Error", err);
}
};
run();
The console.log shows data.Payload as:
Payload: {
[Symbol(Symbol.asyncIterator)]: [AsyncGeneratorFunction: [Symbol.asyncIterator]]
}
what should I do with that?
I was stuck on this exact same issue for quite some time. It looks like the best option now is to append a promise() to it.
So far, I've made progress using the following (sorry, this is incomplete but should at least enable you to read data):
try {
const s3Data = await s3.selectObjectContent(params3).promise();
// using 'any' here temporarily, but will need to address type issues
const events: any = s3Data.Payload;
for await (const event of events) {
try {
if(event?.Records) {
if (event?.Records?.Payload) {
const record = decodeURIComponent(event.Records.Payload.toString().replace(/\+|\t/g, ' '));
records.push(record);
} else {
console.log('skipped event, payload: ', event?.Records?.Payload);
}
}
else if (event.Stats) {
console.log(`Processed ${event.Stats.Details.BytesProcessed} bytes`);
} else if (event.End) {
console.log('SelectObjectContent completed');
}
}
catch (err) {
if (err instanceof TypeError) {
console.log('error in events: ', err);
throw err;
}
}
}
}
catch (err) {
console.log('error fetching data: ', err);
throw err;
}
console.log("final records: ", records);
return records;
}

ResourceNotFoundException: user pool xyz does not exist - Cognito adminConfirmSignup

For some reason, only the adminConfirmSignup gives the user pool does not exist error. The CognitoUser doesn't give that error.
Please refer to the code below:
let cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
var cognitoAdmin = new AWS.CognitoIdentityServiceProvider({ region: process.env.COGNITO_POOL_REGION! });
await cognitoAdmin.adminConfirmSignUp(confirmParams, async(err, data) => { //Only this gives the user pool does not exist error
if (err) {
console.log(`This is the admin user confirm error ---> ${err}`)
} else {
console.log(`Entered else`);
await cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: async(result) => {
cognitoUser.changePassword(resetDetails.currentPassword, resetDetails.newPassword, (err, data) => {
if (err) {
reject(err);
} else {
console.log(`This is the success response of cognito change password -----> ${JSON.stringify(data)}`);
resolve(data);
}
})
},
onFailure: (error) => {
console.log(`This is the onFailure error ----> ${JSON.stringify(error)}`);
reject(error);
}
})
}
})
The password reset works if I use the CognitoUser methods (when I manually confirm the user and use only the cognitoUser methods to authenticate and reset the password).
Your param UserPoolId in SDK calls is incorrect. Won't be able to see that with only what you have posted.
Need another await in front of calling that function
await cognitoUser.changePassword(...)
like this

Making a distinction between file not present and access denied while accessing s3 object via Javascript

I have inherited the following code. This is part of CICD pipeline. It tries to get an object called "changes" from a bucket and does something with it. If it is able to grab the object, it sends a success message back to pipeline. If it fails to grab the file for whatever reason, it sends a failure message back to codepipeline.
This "changes" file is made in previous step of the codepipeline. However, sometimes it is valid for this file NOT to exist (i.e. when there IS no change).
Currently, the following code makes no distinction if file simply does not exist OR some reason code failed to get it (access denied etc.)
Desired:
I would like to send a success message back to codepipeline if file is simply not there.
If there is access issue , then the current outcome of "failure' would still be valid.
Any help is greatly appreciated. Unfortunately I am not good enough with Javascript to have any ideas to try.
RELEVANT PARTS OF THE CODE
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const lambda = new AWS.Lambda();
const codePipeline = new AWS.CodePipeline();
// GET THESE FROM ENV Variables
const {
API_SOURCE_S3_BUCKET: s3Bucket,
ENV: env
} = process.env;
const jobSuccess = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobSuccessResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
const jobFailure = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobFailureResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
// MAIN CALLER FUNCTION. STARTING POINT
exports.handler = async (event, context, callback) => {
try {
// WHAT IS IN changes file in S3
let changesFile = await getObject(s3, s3Bucket, `lambda/${version}/changes`);
let changes = changesFile.trim().split("\n");
console.log("List of Changes");
console.log(changes);
let params = { jobId };
let jobSuccessResponse = await jobSuccess(codePipeline, params);
context.succeed("Job Success");
}
catch (exception) {
let message = "Job Failure (General)";
let failureParams = {
jobId,
failureDetails: {
message: JSON.stringify(message),
type: "JobFailed",
externalExecutionId: context.invokeid
}
};
let jobFailureResponse = await jobFailure(codePipeline, failureParams);
console.log(message, exception);
context.fail(`${message}: ${exception}`);
}
};
S3 should return an error code in the exception:
The ones you care about are below:
AccessDenied - Access Denied
NoSuchKey - The specified key does not exist.
So in your catch block you should be able to validate exception.code to check if it matches one of these 2.

ECONRESET socket hungup

I have a function that triggers on firebase database onWrite. The function body use two google cloud apis (DNS and Storage).
While the function is running and working as expected (mostly), the issue is that the Socket hang up more often than I'd like. (50%~ of times)
My questions are:
Is it similar to what the rest of the testers have experienced? Is it a well known issue that is outstanding or expected behavior?
the example code is as follows:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {credentials} = functions.config().auth;
credentials.private_key = credentials.private_key.replace(/\\n/g, '\n');
const config = Object.assign({}, functions.config().firebase, {credentials});
admin.initializeApp(config);
const gcs = require('#google-cloud/storage')({credentials});
const dns = require('#google-cloud/dns')({credentials});
const zoneName = 'applambda';
const zone = dns.zone(zoneName);
exports.createDeleteDNSAndStorage = functions.database.ref('/apps/{uid}/{appid}/name')
.onWrite(event => {
// Only edit data when it is first created.
const {uid, appid} = event.params;
const name = event.data.val();
const dbRef = admin.database().ref(`/apps/${uid}/${appid}`);
if (event.data.previous.exists()) {
console.log(`already exists ${uid}/${appid}`);
return;
}
// Exit when the data is deleted.
if (!event.data.exists()) {
console.log(`data is being deleted ${uid}/${appid}`);
return;
}
const url = `${name}.${zoneName}.com`;
console.log(`data: ${uid}/${appid}/${name}\nsetting up: ${url}`);
setupDNS({url, dbRef});
setupStorage({url, dbRef});
return;
});
function setupDNS({url, dbRef}) {
// Create an NS record.
let cnameRecord = zone.record('cname', {
name: `${url}.`,
data: 'c.storage.googleapis.com.',
ttl: 3000
});
zone.addRecords(cnameRecord).then(function() {
console.log(`done setting up zonerecord for ${url}`);
dbRef.update({dns: url}).then(res => console.log(res)).catch(err => console.log(err));
}).catch(function(err) {
console.error(`error setting up zonerecord for ${url}`);
console.error(err);
});
}
function setupStorage({url, dbRef}) {
console.log(`setting up storage bucket for ${url}`);
gcs.createBucket(url, {
website: {
mainPageSuffix: `https://${url}`,
notFoundPage: `https://${url}/404.html`
}
}).then(function(res) {
let bucket = res[0];
console.log(`created bucket ${url}, setting it as public`);
dbRef.update({storage: url}).then(function() {
console.log(`done setting up bucket for ${url}`);
}).catch(function(err) {
console.error(`db update for storage failed ${url}`);
console.error(err);
});
bucket.makePublic().then(function() {
console.log(`bucket set as public for ${url}`);
}).catch(function(err) {
console.error(`setting public for storage failed ${url}`);
console.error(err);
});
}).catch(function(err) {
console.error(`creating bucket failed ${url}`);
console.error(err);
});
}
I'm thinking your function needs to return a promise so that all the other async work has time to complete before the function shuts down. As it's shown now, your functions simply returns immediately without waiting for the work to complete.
I don't know the cloud APIs you're using very well, but I'd guess that you should make your setupDns() and setupStorage() return the promises from the async work that they're doing, then return Promise.all() passing those two promises to let Cloud Functions know it should wait until all that work is complete before cleaning up the container that's running the function.

Categories