Skip to content
This repository was archived by the owner on Jan 20, 2020. It is now read-only.

Commit 95eb487

Browse files
author
Luciano Nooijen
authored
Merge pull request #34 from BytecodeOpenSource/cli
Added basic CLI
2 parents dce7612 + 91fc535 commit 95eb487

File tree

7 files changed

+456
-38
lines changed

7 files changed

+456
-38
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ script:
3030
- yarn run test
3131
- yarn run coverage
3232
- codecov
33-
- yarn run build
33+
# - yarn run build -- Not needed yet
3434

3535
cache:
3636
yarn: true

README.md

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const nodeBlog = require('nodejs-blog');
3636
const { authors, auth, users, categories, articles } = require('nodejs-blog');
3737
```
3838

39-
to start using the package, create a new instance of the NodeBlog class
39+
to start using the package, create a new blog object:
4040

4141
```js
4242
const client = 'YOUR_DB_CLIENT'; // for more info, see https://knexjs.org/
@@ -99,11 +99,11 @@ articles.delete(blog, id)
9999

100100
We recommend creating a single file that will create the NodeBlog instance, and `export` this instance, and `import` in all other files where you want to use NodeJS Blog.
101101

102-
For security reasons we recommend using environment variables for loading the configuration. This is also in compliance with the [12 factor app Config guidelines](https://12factor.net/config)
102+
For security reasons we recommend using environment variables for loading the configuration. This is also in compliance with the [12 factor app Config guidelines](https://12factor.net/config).
103103

104104
Note: NodeJS blog was made to be used with PostgreSQL, but it should(/could) also be compatible with other databases, as it uses [KnexJS](https://knexjs.org) under the hood.
105105

106-
*A demo application is currently in development*
106+
*A demo application and a standalone CLI are currently in development*
107107

108108
## Running the API as a standalone service (still in development, might not work 100%)
109109

@@ -124,6 +124,7 @@ For development, the following commands are available:
124124
| Command | Functionality |
125125
| - | - |
126126
| `yarn run dev` | Runs a `nodemon` server for the `server/server.js` file, and exposing the standalone service to your `localhost` |
127+
| `yarn run cli` | Runs the CLI tool created for simple CRUD operations without accessing the database directly |
127128
| `yarn run lint` | Runs ESLint, for PRs this should always pass |
128129
| `yarn run test` | Runs Jest once, for PRs this should always pass. Your database must be available as it is used to run tests on (beware: all existing data will be wiped, we recommend using a separate test-database, this can be set in the `.env` file) |
129130
| `yarn run test:watch` | Same as `yarn run test`, but it Jest watches for changes |
@@ -132,26 +133,6 @@ For development, the following commands are available:
132133
| `yarn run reinstall` | Deletes the `node_modules/` folder and reinstalls everything, if you get some stange dependency errors, run this command |
133134
| `yarn run clean` | Deletes folders `build/`, `dist/` and `coverage/` |
134135

135-
### Folder structure
136-
137-
```md
138-
.
139-
├── controllers Controllers for the module based API
140-
├── database All database related files
141-
│   ├── migrations
142-
│   └── seeds
143-
├── helpers Helper files, for example: logger, database instance
144-
├── server
145-
│   ├── controllers
146-
│   ├── middleware
147-
│   │   └── modules
148-
│   └── routes
149-
├── src Source directory for the API exposed by the module
150-
└── tests All tests written
151-
├── config
152-
└── database
153-
```
154-
155136
### Node Environments
156137

157138
The following NodeJS Environments are integrated:

cli/add-post.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* tslint:disable: no-console */
2+
3+
import chalk from 'chalk';
4+
import inquirer from 'inquirer';
5+
import articles = require('../controllers/articles');
6+
7+
const { addArticle, getArticle } = articles;
8+
9+
const prompt = inquirer.createPromptModule();
10+
11+
const addPostQuestions = [
12+
{ type: 'input', name: 'title', message: 'What is the post title' },
13+
{ type: 'input', name: 'subtitle', message: 'And the post subtitle' },
14+
{ type: 'input', name: 'slug', message: 'And the post slug' },
15+
{ type: 'input', name: 'author', message: 'What is the author id' },
16+
{ type: 'input', name: 'category', message: 'What is the category id' },
17+
{ type: 'input', name: 'summary', message: 'Please give a short summary' },
18+
{ type: 'input', name: 'image_url', message: 'What is the image url' },
19+
{ type: 'input', name: 'html_content', message: 'Minified HTML string' },
20+
{ type: 'input', name: 'related_articles',
21+
message: 'Related articles, comma seperated, no spaces, ("1,2,3")' },
22+
];
23+
24+
const createPostDataObject = input => {
25+
const relatedArticleString = input.related_articles;
26+
const relatedArticlesArray = relatedArticleString.split(',');
27+
const postData = { ...input, related_articles: relatedArticlesArray };
28+
return postData;
29+
};
30+
31+
const addPost = async knex => {
32+
const postDataInputRaw = await prompt(addPostQuestions);
33+
const postDataInput = createPostDataObject(postDataInputRaw);
34+
try {
35+
const addedArticle = await addArticle(knex, postDataInput);
36+
console.log(`${chalk.green('Success')}: article added!`);
37+
} catch (err) {
38+
console.log(`${chalk.red('Error')}: the CLI failed you :(`);
39+
console.log(err);
40+
}
41+
};
42+
43+
export default addPost;

cli/index.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* tslint:disable: no-console import-name */
2+
3+
/**
4+
* TODO: Test this and split up
5+
* We know the rules. Don't write production code if there are no failing tests
6+
* for the code you're about to write. Any code you write that has no unit tests
7+
* should be seen as legacy code. Yes, we get it. This CLI tool should indeed be
8+
* tested, but due to time concerns we chose to set this up quick and easy.
9+
* Besides, we would like to split this up into a separate module soon.
10+
*/
11+
12+
import chalk from 'chalk';
13+
import figlet from 'figlet';
14+
import inquirer from 'inquirer';
15+
import knexGenerator from 'knex';
16+
17+
import generateKnexfile = require('../database/generate-knexfile');
18+
import addPost from './add-post';
19+
20+
const prompt = inquirer.createPromptModule();
21+
const choices = ['Add post'];
22+
const question = {
23+
choices,
24+
name: 'action',
25+
type: 'list',
26+
message: 'Please select the action you wish to perform',
27+
};
28+
29+
const intro = () => {
30+
console.clear();
31+
console.log(
32+
chalk.yellow(
33+
figlet.textSync('NodeJS Blog', { horizontalLayout: 'full' })));
34+
};
35+
36+
const promptHandler = async knex => {
37+
const answer = await prompt([question]) as any; // Hack to avoid error...
38+
if (answer.action === 'Add post') {
39+
addPost(knex);
40+
}
41+
};
42+
43+
const createKnexInstance = config => {
44+
const knexfile = generateKnexfile(config);
45+
const knex = knexGenerator(knexfile);
46+
return knex;
47+
};
48+
49+
const cli = async (
50+
client = process.env.DB_CLIENT_TEST,
51+
host = process.env.DB_HOST_TEST,
52+
user = process.env.DB_USER_TEST,
53+
database = process.env.DB_NAME_TEST,
54+
password = process.env.DB_PASS_TEST,
55+
debug = process.env.KNEX_DEBUG === 'true',
56+
) => {
57+
intro();
58+
59+
try {
60+
// TODO: Load the config using a promt
61+
const config = { client, host, user, database, password, debug };
62+
const knex = createKnexInstance(config);
63+
await promptHandler(knex);
64+
} catch (err) {
65+
console.log(`${chalk.red('Error')}: the CLI failed you :(`);
66+
console.log(err);
67+
}
68+
};
69+
70+
// Run CLI
71+
require('dotenv').config(); // Load .env file
72+
cli();

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
},
3535
"scripts": {
3636
"dev": "nodemon",
37+
"cli": "ts-node cli/index.ts",
3738
"start": "ts-node server/server.ts",
38-
"build": "tsc",
39+
"build": "yarn run clean && tsc",
3940
"lint": "yarn run lint:js && yarn run lint:ts",
4041
"lint:js": "eslint --ext .js,.jsx .",
4142
"lint:ts": "tslint --project .",
@@ -65,10 +66,13 @@
6566
"devDependencies": {
6667
"@types/eslint": "^4.16.4",
6768
"@types/express": "^4.16.0",
69+
"@types/figlet": "^1.2.0",
70+
"@types/inquirer": "^0.0.43",
6871
"@types/jest": "^23.3.9",
6972
"@types/knex": "^0.15.1",
7073
"@types/node": "^10.12.9",
7174
"babel-eslint": "^10.0.1",
75+
"clui": "^0.3.6",
7276
"eslint": "^4.19.1",
7377
"eslint-config-airbnb": "^16.1.0",
7478
"eslint-plugin-import": "^2.12.0",
@@ -78,7 +82,10 @@
7882
"eslint-plugin-prettier": "^3.0.0",
7983
"eslint-plugin-react": "^7.8.2",
8084
"eslint-plugin-security": "^1.4.0",
85+
"figlet": "^1.2.1",
86+
"inquirer": "^6.2.1",
8187
"jest": "^23.6.0",
88+
"minimist": "^1.2.0",
8289
"nodemon": "^1.18.6",
8390
"prettier": "^1.15.2",
8491
"prettier-eslint": "^8.8.2",

tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"compilerOptions": {
33
/* Basic Options */
4-
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
4+
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
55
"lib": ["es2015"],
6-
"module": "amd", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6+
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
77
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
88
"declaration": true, /* Generates corresponding '.d.ts' file. */
99
"sourceMap": true, /* Generates corresponding '.map' file. */

0 commit comments

Comments
 (0)