Manual approval task in step function - invoke through a script - node.js

I have a step function with an activity task that should wait for an input from a script that I will run from my terminal. If the script is invoked, the task that is waiting should get succeeded
Could someone provide examples on how to achieve this?
Any helpful documentations or referenced code links are appreciated.
Would I need an activity worker to invoke this?
Can a script running in my terminal invoke the lambda and mark it as succeeded?
node report-choice-step-success.js --stepfunction-arn <SFN-EXEC> --step-name ManualTask
Script report-choice-step-success.js
const main = () => {
let sfnClient;
const rolename = `StepFunctionExecuter-LOCAL`;
return getcreds({ accountId: '123456789012', region: 'us-east-1', rolename })
.then(params => {
sfnClient = new AWS.StepFunctions(params)
})
.then(() => startmystepfunction(sfnClient));
};
const startmystepfunction = (sfnClient) => {
const stateMachineArn = `arn:aws:states:us-east-1:123456789012:stateMachine:MYSTEPFUNCTION`;
const name = `Manual step`;
const executionParams = { name, stateMachineArn };
return sfnClient.startExecution(executionParams).promise()
.then(response => {
if (response && response.executionArn) {
print(`Started SFN execution for arn: ${response.executionArn}`);)
};
How should I modify the task so that it waits for a manual input so that it gets succeeded?
{
"Comment": "My state machine",
"StartAt": "Manual step",
"States": {
"ManualStep": {
"Type": "Task",
"Resource": "arn:aws:states:::activity:manualtask",
"End": true
}
}
}

I could get the Activity ARN from the executionARN using the getExecutionHistory method and filtered the scheduled activities.
Then I used that particular activityARN to getActivityTask and then used the sendTaskSuccess method to transition the step to succeeded.
sfnClient.getActivityTask(params).promise()
.then(data => {
var params = {
output: data.input.toString(),
taskToken: data.taskToken.toString()
};
sfnClient.sendTaskSuccess(params).promise()
}).catch(err => {
console.error(err);
console.error(err.stack);
});

Related

Error: No Contract deployed with name Ebay

Getting this error while testing my Ebay Contract
ITs An Ebay Smart Contract Where Products are being Auctioned
yarn run v1.22.15
warning package.json: No license field
$ "E:\Block Chain Projects\30_SMARTCONTRACTS.sol\A Way To Testing\node_modules\.bin\hardhat" test
Ebay Smart Contract
Upload Product function
This is deployer0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
1) "before each" hook for "if the base Price is less than 0 or invalid should revert"
0 passing (2s)
1 failing
1) Ebay Smart Contract
"before each" hook for "if the base Price is less than 0 or invalid should revert":
Error: No Contract deployed with name Ebay
at Object.getContract (node_modules\#nomiclabs\hardhat-ethers\src\internal\helpers.ts:447:11)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Context.<anonymous> (test\Test.js:18:12)
error Command failed with exit code 1.
MY TEST.js
//it
//describe
//beforeEach
const { expect, assert } = require("chai");
const { ethers, getNamedAccounts } = require("hardhat");
describe("Ebay Smart Contract", async function() {
let Ebay;
let deployer;
const product = {
name: "CYCLE",
description: "gare cycles",
BasePrice: "5",
};
beforeEach(async () => {
deployer = (await getNamedAccounts()).deployer;
console.log("This is deployer" + deployer);
Ebay = await ethers.getContract("Ebay", deployer);
});
describe("Upload Product function", async function() {
it("if the base Price is less than 0 or invalid should revert", async function() {
expect(
Ebay.UploadProduct(product.name, product.description, product.BasePrice)
).to.be.revertedWith(Base_Price_cannot_be_Zero());
});
});
});
My deploy.js
const { ethers, deployments } = require("hardhat");
require("dotenv").config();
async function main() {
// const { deploy, log } = deployments;
//const { deployer } = await getNamedAccounts();
const Auction = await ethers.getContractFactory("Ebay");
const auction = await Auction.deploy();
await auction.deployed();
console.log(auction.address);
// const Auction = await deploy("Auctionbargain", {
// from: deployer,
// args: [],
// log: true,
// waitconfirmations: network.config.blockConfirmations || 1,
// });
//console.log(Auction.address);
//log("------------------------------------");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.log(error);
process.exit(1);
});
hardhatconfig.js
require("#nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");
require("dotenv").config();
/** #type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
compilers: [{ version: "0.6.6" }, { version: "0.8.8" }],
},
networks: {
hardhat: {
chainId: 31337,
},
// goerli: {
// url: GOERLI_RPC_URL,
// accounts: [PRIVATE_KEY],
// chainId: 5,
// blockConfirmations: 6,
// },
},
namedAccounts: {
deployer: {
default: 0, // here this will by default take the first account as deployer
},
},
};
It was correctly deployed but gave error everytime i try to test it
I Tried using ABI in place of deployer in my test.js but it did give me the same error
Are you sure you deployed the contract on the same network on which you’re testing? it says:
Error: No Contract deployed with name Ebay
this can happen if you‘re testing on another network where the contract isn’t deployed at.
Also consider that the contract is really named Ebay. I can’t check that since you didn’t provide the solidity file.

RxJS non-blocking pooling

I'm working with Amazon Transcribe Service and in the SDK doesn't have any way to get when the transcription job has finished.
So we need to pool that information. Nowdays we have the following working code...
const jobid = nanoid();
await amazonTrascribeClient
.startTranscriptionJob({
IdentifyMultipleLanguages: true,
TranscriptionJobName: jobId,
Media: {
MediaFileUri: "s3://file-location",
},
Subtitles: {
OutputStartIndex: 1,
Formats: ["vtt", "srt"],
},
OutputBucketName: `file-location`,
OutputKey: `transcriptions/${jobId}/`,
})
.promise();
// HELP HERE:
const callIntervalFunc = () => {
const callInverval = setInterval(async () => {
const { TranscriptionJob } = await amazonTrascribeClient
.getTranscriptionJob({ TranscriptionJobName: jobId })
.promise();
if (
["COMPLETED", "FAILED"].includes(TranscriptionJob.TranscriptionJobStatus)
) {
clearInterval(callInverval);
// Persist in database etc...
}
}, 2000);
};
callIntervalFunc();
But as you can see it's extremally expensive and don't work in the concurrency mode since it's lock the thread. The objective is pool the information without block the event loop, some people said to use fire and forget, but I have no idea where I should start.

Why Hook is called in all update services methods

I'm create a hook file with the following information, which is Hooks.js
Hooks.js is working to authenticate an actions with JWT when need it, I dont need it in all servies calls.
As my understanding the syntax to call a hook was app/use route/hooks and those hooks were only applied to and specific route and not globally.
module.exports = {
errorHandler: (context) => {
if (context.error) {
context.error.stack = null;
return context;
}
},
isValidToken: (context) => {
const token = context.params.headers.authorization;
const payload = Auth.validateToken(token);
console.log(payload);
if(payload !== "Invalid" && payload !== "No Token Provided"){
context.data = payload._id;
}
else {
throw new errors.NotAuthenticated('Authentication Error Token');
}
},
isValidDomain: (context) => {
if (
config.DOMAINS_WHITE_LIST.includes(
context.params.headers.origin || context.params.headers.host
)
) {
return context;
}
throw new errors.NotAuthenticated("Not Authenticated Domain");
},
normalizedId: (context) => {
context.id = context.id || context.params.route.id;
},
normalizedCode: (context) => {
context.id = context.params.route.code;
},
};
Then I create a file for services and routes, like the following:
const Hooks = require("../../Hooks/Hooks");
const userServices = require("./user.services");
module.exports = (app) => {
app
.use("/users", {
find: userServices.find,
create: userServices.createUser,
})
.hooks({
before: {
find: [Hooks.isValidDomain],
create: [Hooks.isValidDomain],
},
});
app
.use("/users/:code/validate", {
update: userServices.validateCode,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.normalizedCode],
},
});
app
.use("/users/personal", {
update: userServices.personalInfo,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.isValidToken],
},
});
};
Why Hooks.isValidToken applies to all my update methods? Even if I'm not calling it?
Please help.
app.hooks registers an application level hook which runs for all services. If you only want it for a specific service and method it needs to be app.service('users').hooks().

Unable to write item(s) to DynamoDB table utilizing DocumentClient - Nodejs

I'm absolutely brand new to DynamoDb and I'm trying to simply write an object from a NodeJS Lambda. Based on what I've read and researched I should probably be using DocumentClient from the aws-sdk. I also found the following question here regarding issues with DocumentClient, but it doesn't seem to address my specific issue....which I can't really find/pinpoint unfortunately. I've set up a debugger to help with SAM local development, but it appears to be only providing some of the errors.
The code's implementation is shown here.
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": {"S": randstring.generate(9)},
"School":{"S": team_name},
"Seed": {"S": seed},
"ESPN_Id": {"S": espn_id}
}
}
console.log(JSON.stringify(params))
dynamodb.put(params, (error,data) => {
if (error) {
console.log("Error ", error)
} else {
console.log("Success! ", data)
}
})
Basically I'm scrubbing a website utilizing cheerio library and cherry picking values from the DOM and saving them into the json object shown below.
{
"TableName": "March-Madness-Teams",
"Item": {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
When I attempt to push this json object to Dynamo, I get errors says
Error MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Item' in params
The above error is all good in well....I assume it didn't like the fact that I had wrapped those to keys in strings, so I removed the quotes and sent the following
{
TableName: "March-Madness-Teams",
Item: {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
However, when I do that...I kind of get nothing.
Here is a larger code snippet.
return new Promise((resolve,reject) => {
axios.get('http://www.espn.com/mens-college-basketball/bracketology')
.then(html => {
const dynamodb = new aws.DynamoDB.DocumentClient()
let $ = cheerio.load(html.data)
$('.region').each(async function(index, element){
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(async function(index2, element2){
var seed = $(element2).siblings('span.rank').text()
if (seed.length > 2){
seed = $(element2).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element2).attr('href').split('/').slice(-2)[0]
var team_name = $(element2).text()
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School":team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
}
console.log(JSON.stringify(params))
// dynamodb.put(params)
// .then(function(data) {
// console.log(`Success`, data)
// })
})
})
})
})
Can you try without the type?
Instead of
"School":{"S": team_name},
for example, use
"School": team_name,
From your code, I can see the mis promise on the dynamodb request. Try to change your lines :
dynamodb.put(params).then(function(data) {
console.log(`Success`, data)
})
to be :
dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
you can combine with await too :
await dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
exports.lambdaHandler = async (event, context) => {
const html = await axios.get('http://www.espn.com/mens-college-basketball/bracketology')
let $ = cheerio.load(html.data)
const schools = buildCompleteSchoolObject(html, $)
try {
await writeSchoolsToDynamo(schools)
return { statusCode: 200 }
} catch (error) {
return { statusCode: 400, message: error.message }
}
}
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(async school => {
await dynamodb.put(school).promise()
})
await Promise.all(promises)
}
const buildCompleteSchoolObject = (html, $) => {
const schools = []
$('.region').each(loopThroughSubRegions(schools, $))
return schools
}
const loopThroughSubRegions = (schools, $) => {
return (index, element) => {
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(populateSchoolObjects(schools, $))
}
}
const populateSchoolObjects = (schools, $) => {
return (index, element) => {
var seed = $(element).siblings('span.rank').text()
if (seed.length > 2) {
seed = $(element).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element).attr('href').split('/').slice(-2)[0]
var team_name = $(element).text()
schools.push({
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School": team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
})
}
}
I know this is drastically different from what I started with but I did some more digging and kind of kind of worked to this...I'm not sure if this is the best way, but I seemed to get it to work...Let me know if something should change!
Oh I understand what you want.
Maybe you can see the code above works, but there is one concept you have to improve here about async - await and promise especially on lambda function.
I have some notes here from your code above, maybe can be your consideration to improve your lambda :
Using await for every promise in lambda is not the best approach because we know the lambda time limitation. But sometimes we can do that for other case.
Maybe you can change the dynamodb.put method to be dynamodb.batchWriteItem :
The BatchWriteItem operation puts or deletes multiple items in one or more tables.
Or If you have to use dynamodb.put instead, try to get improve the code to be like so :
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(school => {
dynamodb.put(school).promise()
})
return Promise.all(promises)
}

Get Operation by Name in Google Speech to Text, with nodejs

I'm using Google Speech to Text Node.js client.
When starting long-running speech transcription, I need to split the operation in two steps.
1. Start the transcription, and return the "name".
2. Ask for the status of the operation.
Ie replicating https://cloud.google.com/speech-to-text/docs/async-recognize#speech-async-recognize-gcs-protocol in nodejs.
My problem is to figure out how to create an OperationsClient, and to make the getOperation({name}) to actually return the result of the transcription.
I do run this code inside two firebase cloud functions:
1. The one that starts the trancscription, and then return the "name". This one works really well with the SpeechClient.
2. Another one that should call operationsClient.getOperation aka the same as curl to "https://speech.googleapis.com/v1/operations/"
import gax, {GrpcClient, GrpcClientOptions, lro} from "google-gax";
const gaxOpts = {
clientConfig: {}
}
const gaxGrpc = new GrpcClient(gaxOpts);
...
app.get('/operations/:googleSpeechRef', async (req, res) => {
const googleSpeechRef = req.params.googleSpeechRef;
const operationsClient = lro({
auth: gaxGrpc.auth,
grpc: gaxGrpc.grpc,
}).operationsClient(gaxOpts);
try {
const [responses] = await operationsClient.getOperation({name: googleSpeechRef},{});
if (responses) {
const operation = responses[0]
console.log("getOperation. responses: ", responses)
const initialApiResponse = responses[1]
operation
.on("complete", (longRunningRecognizeResponse /*, longRunningRecognizeMetadata, finalApiResponse*/) => {
// Adding a listener for the "complete" event starts polling for the
// completion of the operation.
const speechRecognitionResults = longRunningRecognizeResponse.results as ISpeechRecognitionResult[]
// resolve(speechRecognitionResults)
console.log("complete: ", speechRecognitionResults)
})
.on("progress", async (longRunningRecognizeMetadata /*, apiResponse*/) => {
// Adding a listener for the "progress" event causes the callback to be
// called on any change in metadata when the operation is polled.
const percent = longRunningRecognizeMetadata.progressPercent
console.log("progress. Percent", longRunningRecognizeMetadata.progressPercent /*, apiResponse*/)
})
.on("error", (error: Error) => {
// Adding a listener for the "error" event handles any errors found during polling.
// reject(error)
console.log("error: ", error)
})
res.contentType("application/json").status(200).send(JSON.stringify(initialApiResponse))
} else {
res.send(404)
}
} catch (error) {
console.error("Failed to fetch operation by googleSpeechRef: ", googleSpeechRef, ". Error: ", error);
res.status(500).send(serializeError(error))
}
}
The error i get is:
{
"code": 13,
"details": "Not enough responses received",
"metadata":{
"internalRepr":{}
},
"note": "Exception occurred in retry method that was not classified as transient",
"name": "Error",
"message": "Not enough responses received",
"stack": "Error: Not enough responses received\n at Http2CallStream.call.on (/srv/node_modules/#grpc/grpc-js/build/src/client.js:102:45)\n at emitOne (events.js:121:20)\n at Http2CallStream.emit (events.js:211:7)\n at Http2CallStream.endCall (/srv/node_modules/#grpc/grpc-js/build/src/call-stream.js:74:18)\n at /srv/node_modules/#grpc/grpc-js/build/src/call-stream.js:355:18\n at <anonymous>\n at process._tickDomainCallback (internal/process/next_tick.js:229:7)"
}
For anybody finding this question later. My challenge was solved by this answer https://github.com/googleapis/nodejs-speech/issues/10#issuecomment-415900469
const { google } = require('googleapis');
(async () => {
const auth = await google.auth.getClient({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
});
const { data } = await google.speech('v1').operations.get({ auth, name: OPERATION_NAME });
console.log(data);
})();

Resources