Commander.js display help when called with no commands - javascript

I'm using commander.js to write a simple node.js program that interacts with an API. All calls require the use of subcommands. For example:
apicommand get
Is called as follows:
program
.version('1.0.0')
.command('get [accountId]')
.description('retrieves account info for the specified account')
.option('-v, --verbose', 'display extended logging information')
.action(getAccount);
What I want to do now is display a default message when apicommand is called without any subcommands. Just like when you call git without a subcommand:
MacBook-Air:Desktop username$ git
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
These are common Git commands used in various situations:
start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
...

You can do something like this by checking what arguments were received and if nothing other than node and <app>.js then display the help text.
program
.version('1.0.0')
.command('get [accountId]')
.description('retrieves account info for the specified account')
.option('-v, --verbose', 'display extended logging information')
.action(getAccount)
.parse(process.argv)
if (process.argv.length < 3) {
program.help()
}

When you're trying to pass a command, it stores the commands in process.argv array.
You can add a conditon like at the end of your code like -:
if(process.argv.length <= 2)
console.log(program.help());
else
program.parse();

What I want to do now is display a default message when apicommand is called without any subcommands. Just like when you call git without a subcommand
The help is automatically displayed from Commander 5 onwards if you call without a subcommand.
(Disclosure: I am a maintainer of Commander.)

Related

How to write the argument of a function in the terminal with node js

I'm using this code to connect to mailchimp API, get a list of members and put all their email adresses in an array:
var mailchimpMarketing = require("#mailchimp/mailchimp_marketing");
mailchimpMarketing.setConfig({
apiKey: "MY API KEY",
server: "MY SERVER",
});
async function getArrayEmailMembersFromMailchimpListID(listID){
const response = await mailchimpMarketing.lists.getListMembersInfo(listID);
const emailsMailchimp = response.members.map(member => member.email_address);
console.log(emailsMailchimp)
return emailsMailchimp;
}
getArrayEmailMembersFromMailchimpListID("MY LIST ID")
My problem is that I want to write the list ID "MY LIST ID" in my terminal and not in my code when I'm starting the script. Something like that:
$node test.js MyListID
Instead of
$node test.js
But I don't know how to do it.
I think it's possible with process.argv or minimist but I don't understand how they work. Can someone explain it to me or is their any other possibility ?
From the Node-JS v8.x documentation:
The process.argv property returns an array containing the command line
arguments passed when the Node.js process was launched. The first
element will be process.execPath. See process.argv0 if access to the
original value of argv[0] is needed. The second element will be the
path to the JavaScript file being executed. The remaining elements
will be any additional command line arguments.
So in your case you can simply do:
getArrayEmailMembersFromMailchimpListID(process.argv[2])
Of course you should add some error-handling for this to make it more robust.

How to save a document with a dynamic id into Cloud Firestore? Always changing

I am using Cloud Firestore as my database
This is my form codes on my webpage that creates a new document into my Cloud Firestore collection called "esequiz". So how do I code it in such a way that it always plus 1 to the number of documents there are in the database? And also set a limit to having the amount of documents inside the database
form.addEventListener('submit', (e) => {
e.preventDefault();
db.collection('esequiz').add({
question: form.question.value,
right: form.right.value,
wrong: form.wrong.value
});
form.question.value = '';
form.right.value = '';
form.wrong.value = '';
});
It currently works but it will show up as an auto generated ID. How do I make it carry on from the numbers, like as my current documents? When i save I would like it to read the current last document id, OR simply count the number of documents, then just + 1
Insight from Andrei Cusnir, counting documents in Cloud Firestore is not supported.
Now I am trying Andrei's approach 2, to query documents in descending order, then using .limit to retrieve the first one only.
UPDATED
form.addEventListener('submit', (e) => {
e.preventDefault();
let query = db.collection('esequiz');
let getvalue = query.orderBy('id', 'desc').limit(1).get();
let newvalue = getvalue + 1;
db.collection('esequiz').doc(newvalue).set({
question: form.question.value,
right: form.right.value,
wrong: form.wrong.value
});
form.question.value = '';
form.right.value = '';
form.wrong.value = '';
});
No more error, but instead, the code below returns [object Promise]
let getvalue = query.orderBy('id', 'desc').limit(1).get();
So when my form saves, it saves as [object Promise]1, which I don't know why it is like this. Can someone advise me on how to return the document id value instead of [object Promise]
I think it is because I did specify to pull the document id as the value, how do I do so?
UPDATED: FINAL SOLUTION
Played around with the codes from Andrei, and here are the final codes that works. Much thanks to Andrei!
let query = db.collection('esequiz');
//let getvalue = query.orderBy('id', 'desc').limit(1).get();
//let newvalue = getvalue + 1;
query.orderBy('id', 'desc').limit(1).get().then(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
var newID = documentSnapshot.id;
console.log(`Found document at ${documentSnapshot.ref.path}`);
console.log(`Document's ID: ${documentSnapshot.id}`);
var newvalue = parseInt(newID, 10) + 1;
var ToString = ""+ newvalue;
db.collection('esequiz').doc(ToString).set({
id: newvalue,
question: form.question.value,
right: form.right.value,
wrong: form.wrong.value
});
});
});
If I understood correctly you are adding data to the Cloud Firestore and each new document will have as name an incremental number.
If you query all the documents and then count how many are of them, then you are going to end up with many document reads as the database increases. Don't forget that Cloud Firestore is charging per document Read and Write, therefore if you have 100 documents and you want to add new document with ID: 101, then with the approach of first reading all of them and then counting them will cost you 100 Reads and then 1 Write. The next time it will cost you 101 Reads and 1 Write. And it will go on as your database increases.
The way I see is from two different approaches:
Approach 1:
You can have a single document that will hold all the information of the database and what the next name should be.
e.g.
The structure of the database:
esequiz:
0:
last_document: 2
1:
question: "What is 3+3?
right: "6"
wrong: "0"
2:
question: "What is 2+3?
right: "5"
wrong: "0"
So the process will go as follows:
Read document "/esequiz/0" Counts as 1 READ
Create new document with ID: last_document + 1 Counts as 1 WRITE
Update the document that holds the information: last_document = 3; Counts as 1 WRITE
This approach cost you 1 READ and 2 WRITES to the database.
Approach 2:
You can load only the last document from the database and get it's ID.
e.g.
The structure of the database (Same as before, but without the additional doc):
esequiz:
1:
question: "What is 3+3?
right: "6"
wrong: "0"
2:
question: "What is 2+3?
right: "5"
wrong: "0"
So the process will go as follows:
Read the last document using the approach described in Order and limit data with Cloud Firestore documentation. So you can use direction=firestore.Query.DESCENDING with combination of limit(1) which will give you the last document. Counts as 1 READ
Now you know the ID of the loaded document so you can create new document with ID: that will use the loaded value and increase it by 1. Counts as 1 WRITE
This approach cost you 1 READ and 1 WRITE in total to the database.
I hope that this information was helpful and it resolves your issue. Currently counting documents in Cloud Firestore is not supported.
UPDATE
In order for the sorting to work, you will also have to include the id as a filed of the document that so you can be able to order based on it. I have tested the following example and it is working for me:
Structure of database:
esequiz:
1:
id: 1
question: "What is 3+3?
right: "6"
wrong: "0"
2:
id:2
question: "What is 2+3?
right: "5"
wrong: "0"
As you can see the ID is set the same as the document's ID.
Now you can query all the documents and order based on that filed. At the same time you can only retrieve the last document from the query:
const {Firestore} = require('#google-cloud/firestore');
const firestore = new Firestore();
async function getLastDocument(){
let query = firestore.collection('esequiz');
query.orderBy('id', 'desc').limit(1).get().then(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
console.log(`Found document at ${documentSnapshot.ref.path}`);
console.log(`Document's ID: ${documentSnapshot.id}`);
});
});
}
OUTPUT:
Found document at esequiz/2
Document's ID: 2
Then you can take the ID and increase it by 1 to generate the name for your new document!
UPDATE 2
So, the initial question is about "How to store data in the Cloud Firestore with documents having incremental ID", at the moment you are facing issues of setting up Firestore with you project. Unfortunately, the new raised questions should be discussed in another Stackoverflow post as they have nothing to do with the logic of having incremental IDs for the document and it is better to keep one issue per question, to give better community support for members that are looking for a solution about particular issues. Therefore, I will try to help you, in this post, to execute a simple Node.js script and resolve the initial issue, which is storing to Cloud Firestore documents with incremental IDs. Everything else, on how to setup this in your project and how to have this function in your page, should be addressed in additional question, where you also will need to provide as much information as possible about the Framework you are using, the project setup etc.
So, lets make a simple app.js work with the logic described above:
Since you have Cloud Firestore already working, this means that you already have Google Cloud Platform project (where the Firestore relies) and the proper APIs already enabled. Otherwise it wouldn't be working.
Your guide in this tutorial is the Cloud Firestore: Node.js Client documentation. It will help you to understand all the methods you can use with the Firestore Node.js API. You can find helpful links for adding, reading, querying documents and many more operations. (I will post entire working code later in this steps. I just shared the link so you know where to look for additional features)
Go to Google Cloud Console Dashboard page. You should login with your Google account where your project with the Firestore database is setup.
On top right corner you should see 4 buttons and your profile picture. The first button is the Activate Cloud Shell. This will open a terminal on the bottom of the page with linux OS and Google Cloud SDK already install. There you can interact with your resources within GCP projects and test your code locally before using it in your projects.
After clicking that button, you will notice that the terminal will open in the bottom of your page.
To make sure that you are properly authenticated we will set up the project and authenticate the account again, even if it is already done by default. So first execute $ gcloud auth login
On the prompted question type Y and hit enter
Click on the generated link and authenticate your account on the prompted window
Copy the generated string back to the terminal and hit enter. Now you should be properly authenticated.
Then setup the project that contains Cloud Firestore database with the following command: $ gcloud config set project PROJECT_ID. Now you are ready to build a simple app.js script and execute it.
Create a new app.js file: nano app.js
Inside paste my code example that can be found in this GitHub link. It contains fully working example and many comments explaining each part therefore it is better that it is shared through GitHub link and not pasted here. Without doing any modifications, this code will execute exactly what you are trying to do. I have tested it my self and it is working.
Execute the script as: node app.js
This will give you the following error:
Error: Cannot find module '#google-cloud/firestore'
Since we are importing the library #google-cloud/firestore but haven't installed it yet.
Install #google-cloud/firestore library as follows: $ npm i #google-cloud/firestore. Described in DOC.
Execute the script again: $ node app.js.
You should see e.g. Document with ID: 3 is written.
If you execute again, you should see e.g. Document with ID: 4 is written.
All those changes should appear in your Cloud Firestore database as well. As you can see it is loading the ID of the last document, it is creating a new ID and then it creates a new document with the given arguments, while using the new generated ID as document name. This is exactly what the initial issue was about.
So I have shared with you the full code that works and does exactly what you are trying to do. Unfortunately, the other newly raised issues, should be addressed in another Stackoverflow post, as they have nothing to do with the initial issue, which is "How to create documents with incremental ID". I recommend you to follow the steps and have a working example and then try to implement the logic to your project. However, if you are still facing any issues with how to setup Firestore in your project then you can ask another question. After that you can combine both solutions and you will have working app!
Good luck!
I don't think the way you are trying to get the length of the collection is right and I am entirely not sure what is the best way to get that either. Because the method you are trying to implement will cost you a lot more as you are trying to read all the records of the collection.
But there can be alternatives to get the number you require.
Start storing the ID in the record and make the query with limit 1 and a descending sort on ID.
Store the latest number in another collection and increment that every time you create a new record, And fetch the same whenever needed.
These methods might fail if concurrent requests are being made without transactions.

How to make a node call from another node in a package on CONNECT platform?

When developing a CONNECT package, how can you add a call to another node programmatically?
you can easily do that via the javascript API:
const platform = require('connect-platform');
platform.call('/your-path', {
paramA: "some-value",
//
// all other parameters you want to pass. if there are none, leave this empty.
//
});
check this guide for more info on that:
https://medium.com/connect-platform/inline-coding-in-connect-platform-534fce3c8cdf

how to read terminal input in node.js?

I want to do some local node test before push the code to server.
how can I read terminal input as a input for my js script?
readline or something
I think there is no need to use third party library, if you just want to get command line params.
You can use process.argv property of process node core object.
just use process.argv & you are good to go.It returns an array in which by default there is 2 element, at 0 index Node execution directory & at 1 index working directory, so cmd line params start from 2nd index.
So in nutshell, you can access cmd line params using process.argv[2] onwards.
For commandline app you can use nicl. you can make call to your chat module or websocket or whatever based on your logic.
var nicl = require("nicl");
function main() {
nicl.printLine("Hello, what is your name?");
var name = nicl.readLine();
//call to websocket or any chat module
nicl.printLine("Great to meet you, " + name + "!");
process.exit(0);
}
nicl.run(main);

How can one address two instances of the same application through osascript

Can anyone think of a workaround for the osascript index-by-name bottle-neck in its reference to multiple instances of the same application ?
If we obtain two process ids – one for each of two different instances of the same application, osascript returns the same instance in exchange for either pid - as if it first maps the pid to an application name, and then retrieves the first application process with that name.
For example, start two different instances of VLC.app, playing two different video files, with something like:
open -na /Applications/VLC.app ~/fileA.m4v
open -na /Applications/VLC.app ~/fileB.m4v
then obtain the two separate application process ids with, for example:
echo "$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')"
We can then use Applescript or Yosemite JXA Javascript to get a reference to an application object from either pid.
It turns out, however, that whichever process id we supply, we are always returned a reference to the same instance, running the same video file, as if osascript simply translates a pid to an application name, and then always returns the first process which matches that name.
Yosemite Javascript for applications:
function run() {
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var lstVLC = app.doShellScript(
"echo \"$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')\""
).split(/[\r\n]/).map(Number).map(Application);
return {
firstInstance: lstVLC[0].windows[0].name(),
secondInstance: lstVLC[1].windows[0].name()
};
}
Applescript:
on run {}
set strCMD to "echo \"$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')\""
set lstNum to paragraphs of (do shell script strCMD)
repeat with i from 1 to length of lstNum
set item i of lstNum to (item i of lstNum) as number
end repeat
tell application "System Events"
set oProcA to first application process where unix id = (item 1 of lstNum)
set oProcB to first application process where unix id = (item 2 of lstNum)
end tell
return [name of first window of oProcA, name of first window of oProcB]
end run
Any thoughts on a route to scripting each instance separately ?
For each instance, ask the name of the window from the same line as the specific process, like this :
set windowNames to {}
set lstNum to paragraphs of (do shell script "ps -ceo pid=,comm= | awk '/VLC/ { print $1}'")
tell application "System Events" to repeat with i in lstNum
set end of windowNames to name of first window of (first application process where unix id = i)
end repeat
return windowNames
This seems to have been fixed in El Capitan, as your JavaScript code ran fine on my machine.
Using jackjr300's approach in Javascript, to get at least to UI scripting (though not to the Application object interface):
function run() {
var appSE = Application("System Events");
app = Application.currentApplication();
app.includeStandardAdditions = true;
function uiWidgets(lngID) {
return appSE.processes.whose({
unixId: lngID
})[0].windows[0].uiElements();
}
var lstWidgets = app.doShellScript(
"ps -ceo pid=,comm= | awk '/VLC/ { print $1}'"
).split(/\r/).map(Number).map(uiWidgets);
return lstWidgets;
}
JXA is a bundle of bugs and defective design. Its failure to do stuff like this right is depressing, but entirely unsurprising (the AS team has form).
As for AppleScript, it's never provided a direct way to target apps by PID. In the past I may have cheated it by enabling Remote Apple Events and targeting the process with an eppc://USER#HOST/APPNAME?pid=PID URL, but trying it just now on 10.10 damned if I could get it to work as it always returned a "remote access not allowed" error.
Appscript could do this stuff in its sleep, but I dropped public support for it due to Apple's War on Carbon and crappy "replacement" Cocoa APIs forcing it into "legacy" status, so you're on your own there.
The one officially supported option that may work is OS X's Scripting Bridge framework, which provides a method for targeting processes by PID. Though like JXA it's riddled with design flaws, missing features, and application compatibility problems, so YMWV.

Categories