Skip to content

Commit a94fb0c

Browse files
committed
Merge branch 'develop' into feature/fix-windows-issues
# Conflicts: # package.json # src/console/lib/commander.ts
2 parents 47629cc + 6d2f5cc commit a94fb0c

20 files changed

+364
-79
lines changed

README.md

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,104 @@ Try it!! We are happy to hear your feedback or any kind of new features.
4040
- **Integrated Testing Tool** thanks to [Wallaby.js](https://wallabyjs.com/)
4141

4242
## Getting Started
43-
### Prerequisites
44-
* Install [Node.js](http://nodejs.org)
45-
* on OSX use [homebrew](http://brew.sh) `brew install node`
46-
* on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs`
47-
* Install yarn globally `npm install yarn -g`
48-
49-
### Installing
50-
* `fork` this repo
51-
* `clone` your fork
52-
* `cp .env.example .env` to copy the example .env file and enter your database connection
53-
* Create a new database. You will find the name in the .env file.
54-
* Run `npm run setup` or enter the following commands manually:
55-
* `yarn install` to install all dependencies and typings.
56-
* `npm run db:migrate` to create the schema.
57-
* `npm run db:seed` to insert some test data.
58-
* `npm run serve` to start the application.
59-
60-
### Running the app
61-
After you have installed all dependencies you can run the app.
62-
Enter `npm run serve` to start a local server using `nodemon`, which will watch for any file changes and will restart the sever according to these changes.
63-
The server address will be displayed to you as `http://0.0.0.0:3000`.
43+
### Step 1: Set up the Development Environment
44+
You need to set up your development environment before you can do anything.
45+
46+
Install [Node.js and NPM](https://nodejs.org/en/download/)
47+
* on OSX use [homebrew](http://brew.sh) `brew install node`
48+
* on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs`
49+
50+
Install yarn globally
51+
```
52+
npm install yarn -g
53+
```
54+
55+
Install a MySQL database.
56+
57+
> If you work with a mac, we recommend to use homebrew for the installation.
58+
59+
### Step 2: Create new Project
60+
Fork or download this project. Configure your package.json for your new project.
61+
62+
Then copy the `example.env` file and rename it to `.env`. In this file you have to add your database connection information.
63+
64+
Create a new database with the name you have in your `.env`-file.
65+
66+
Then setup your application environment.
67+
```
68+
npm run setup
69+
```
70+
71+
> This installs all dependencies with yarn. After that it migrates the database and seeds some test data into it. So after that your development environment is ready to use.
72+
73+
### Step 3: Serve your App
74+
Go to the project dir and start your app with this npm script.
75+
```
76+
npm run serve
77+
```
78+
79+
> This starts a local server using `nodemon`, which will watch for any file changes and will restart the sever according to these changes.
80+
> The server address will be displayed to you as `http://0.0.0.0:3000`.
81+
82+
### Step 4: Create a new Resource
83+
Go to the project dir and hit this command in your terminal.
84+
```
85+
npm run console make:resource
86+
```
87+
88+
Apply the same information like you see in the screenshot below.
89+
90+
![console](console.png)
91+
92+
> With that you just have created a complete new endpoint in your api for the resource pets.
93+
94+
Normally a pet belogns to a user, so we have to add the relationship between users an pets. Open the created migration file and replace the user property with these lines.
95+
```
96+
table.integer('user_id').unsigned();
97+
table.foreign('user_id').references('id').inTable('users').onDelete('cascade');
98+
```
99+
100+
Next we have to add this relationship also in the pets model.
101+
```
102+
public user(): User {
103+
return this.belongsTo(User);
104+
}
105+
```
106+
107+
> The relationship between the users and pets are set and ready. So you can migrate your database with `npm run db:migrate`
108+
109+
### Step 5: Create a Seeder
110+
To seed some cute pets we need a smart factory. So open the ./src/database/factories/index.ts and add this code.
111+
```
112+
/**
113+
* PET - Factory
114+
*/
115+
factory.define(Pet, (faker: Faker.FakerStatic, args: any[]) => {
116+
const type = args[0];
117+
return {
118+
name: faker.name.firstName(),
119+
type: type || 'dog',
120+
userId: factory.get(User).returning('id')
121+
};
122+
});
123+
```
124+
125+
> This factory helps us to create a fake pet to seed to the database.
126+
127+
Run this command in your terminal and call the new seeder `create pets`.
128+
```
129+
npm run console make:seed
130+
```
131+
132+
Open the file and place this code into it.
133+
```
134+
await factory.get(Pet)
135+
.create(10);
136+
```
137+
138+
> Now we can seed some nice cats into the database with `npm run db:seed`.
139+
140+
> That was easy! Now its your turn to make something great out of it.
64141
65142
## Scripts / Tasks
66143
All script are defined in the package.json file, but the most important ones are listed here.

console.png

99.4 KB
Loading

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "express-typescript-boilerplate",
3-
"version": "2.0.0-beta",
3+
"version": "2.0.0-beta.1",
44
"description": "A delightful way to building a RESTful API with NodeJs & TypeScript",
55
"main": "src/app.ts",
66
"scripts": {
@@ -19,9 +19,9 @@
1919
"db:migrate:rollback": "npm run banner rollback && \"./node_modules/.bin/knex\" migrate:rollback",
2020
"db:seed": "npm run banner seed && \"./node_modules/.bin/knex\" seed:run",
2121
"db:reset": "npm run console db:reset",
22-
"console": "npm run ts-node:fast -- ./src/console/commander.ts",
23-
"console:dev": "npm run ts-node -- ./src/console/commander.ts",
24-
"console:help": "npm run ts-node:fast -- ./src/console/commander.ts --help",
22+
"console": "npm run ts-node:fast -- ./src/console/lib/commander.ts",
23+
"console:dev": "npm run ts-node -- ./src/console/lib/commander.ts",
24+
"console:help": "npm run ts-node:fast -- ./src/console/lib/commander.ts --help",
2525
"lint": "./node_modules/.bin/tslint -c ./tslint.json -p tsconfig.json 'src/**/*.ts' --format stylish",
2626
"transpile": "./node_modules/.bin/tsc",
2727
"clean": "npm run banner clean && npm run clean:dist",

src/console/MakeApiTestCommand.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* MakeSeedCommand
3+
* -------------------------------------
4+
*
5+
*/
6+
import { AbstractMakeCommand } from './lib/AbstractMakeCommand';
7+
8+
9+
export class MakeApiTestCommand extends AbstractMakeCommand {
10+
11+
public static command = 'make:api-test';
12+
public static description = 'Generate new api test';
13+
14+
public target = '/black-box';
15+
public type = 'API Test';
16+
public suffix = '';
17+
public template = 'api-test.hbs';
18+
public updateTargets = false;
19+
public isTest = true;
20+
21+
}

src/console/MakeModelCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class MakeModelCommand extends AbstractMakeCommand {
4747
await super.write();
4848

4949
// Create interface for this resource object
50-
const filePath = buildFilePath('types/resources', this.context.name.camelCase, '.d.ts');
50+
const filePath = buildFilePath('types/resources', this.context.name.camelCase, false, '.d.ts');
5151
await existsFile(filePath, true);
5252
await writeTemplate('resource.hbs', filePath, this.context);
5353
}

src/console/MakeResourceCommand.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { MakeRepoCommand } from './MakeRepoCommand';
1010
import { MakeServiceCommand } from './MakeServiceCommand';
1111
import { MakeControllerCommand } from './MakeControllerCommand';
1212
import { MakeRequestCommand } from './MakeRequestCommand';
13+
import { MakeApiTestCommand } from './MakeApiTestCommand';
1314

1415

1516
export class MakeResourceCommand extends AbstractMakeCommand {
@@ -29,9 +30,10 @@ export class MakeResourceCommand extends AbstractMakeCommand {
2930
public makeControllerCommand: AbstractMakeCommand;
3031
public makeCreateRequestCommand: MakeRequestCommand;
3132
public makeUpdateRequestCommand: MakeRequestCommand;
33+
public makeApiTestCommand: MakeApiTestCommand;
3234

3335
public async run(): Promise<void> {
34-
this.context = await askFileName(this.context, this.type, this.suffix, this.prefix);
36+
this.context = await this.askFileName(this.context, this.type, this.suffix, this.prefix);
3537
this.context.properties = await askProperties(this.context.name);
3638
this.context.hasProperties = true;
3739
this.context.isResourceTemplate = true;
@@ -43,6 +45,7 @@ export class MakeResourceCommand extends AbstractMakeCommand {
4345
this.makeControllerCommand = new MakeControllerCommand(this.context);
4446
this.makeCreateRequestCommand = new MakeRequestCommand(this.context, 'Create');
4547
this.makeUpdateRequestCommand = new MakeRequestCommand(this.context, 'Update');
48+
this.makeApiTestCommand = new MakeApiTestCommand(this.context);
4649

4750
// Ask all meta-data
4851
await this.makeModelCommand.run();
@@ -51,6 +54,7 @@ export class MakeResourceCommand extends AbstractMakeCommand {
5154
await this.makeControllerCommand.run();
5255
await this.makeCreateRequestCommand.run();
5356
await this.makeUpdateRequestCommand.run();
57+
await this.makeApiTestCommand.run();
5458
}
5559

5660
public async write(): Promise<void> {
@@ -60,6 +64,7 @@ export class MakeResourceCommand extends AbstractMakeCommand {
6064
await this.makeControllerCommand.write();
6165
await this.makeCreateRequestCommand.write();
6266
await this.makeUpdateRequestCommand.write();
67+
await this.makeApiTestCommand.write();
6368
}
6469

6570
}

src/console/MakeSeedCommand.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
* -------------------------------------
44
*
55
*/
6+
import * as _ from 'lodash';
67
import { AbstractMakeCommand } from './lib/AbstractMakeCommand';
78

8-
99
export class MakeSeedCommand extends AbstractMakeCommand {
1010

1111
public static command = 'make:seed';
@@ -17,4 +17,10 @@ export class MakeSeedCommand extends AbstractMakeCommand {
1717
public template = 'seed.hbs';
1818
public updateTargets = false;
1919

20+
public parseName(suffix: string = '', prefix: string = ''): (name: string) => string {
21+
return (name: string) => {
22+
return _.snakeCase(name);
23+
};
24+
}
25+
2026
}

src/console/lib/AbstractMakeCommand.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
*
55
*/
66
import * as _ from 'lodash';
7+
import * as path from 'path';
8+
import * as inquirer from 'inquirer';
9+
710
import { writeTemplate } from './template';
8-
import { askFileName, buildFilePath, existsFile, parseName, updateTargets } from './utils';
11+
import { existsFile, parseName, updateTargets, inputIsRequired } from './utils';
912

1013
export interface MakeCommand {
1114
context: any;
@@ -44,20 +47,56 @@ export class AbstractMakeCommand {
4447
public template = 'template.hbs';
4548
public target = 'api/target/path';
4649
public updateTargets = true;
50+
public isTest = false;
4751

4852
constructor(context?: any) {
4953
this.context = _.cloneDeep(context);
5054
}
5155

5256
public async run(): Promise<void> {
53-
this.context = await askFileName(this.context, this.type, this.suffix, this.prefix);
57+
this.context = await this.askFileName(this.context, this.type, this.suffix, this.prefix);
5458
}
5559

5660
public async write(): Promise<void> {
57-
const filePath = buildFilePath(this.target, this.context.name);
58-
await existsFile(filePath, true);
61+
const filePath = this.buildFilePath(this.target, this.context.name, this.isTest);
62+
await existsFile(filePath, true, this.isTest);
5963
this.context.name = parseName(this.context.name, this.suffix);
6064
await writeTemplate(this.template, filePath, this.context);
6165
}
6266

67+
public buildFilePath = (targetPath: string, fileName: string, isTest = false, extension = '.ts') => {
68+
return path.join(__dirname, `/../../${targetPath}`, `${fileName}${extension}`);
69+
}
70+
71+
public parseName(suffix: string = '', prefix: string = ''): (name: string) => string {
72+
return (name: string) => {
73+
let ns = name.split('/');
74+
ns = ns.map((v) => _.camelCase(v));
75+
ns[ns.length - 1] = _.capitalize(ns[ns.length - 1]);
76+
return (ns.join('/')) + prefix + suffix;
77+
};
78+
}
79+
80+
public async askFileName(context: any, name: string, suffix: string, prefix: string): Promise<any> {
81+
const nameParser = this.parseName(suffix, prefix);
82+
if (context === undefined || context.name === undefined) {
83+
const prompt = inquirer.createPromptModule();
84+
context = await prompt([
85+
{
86+
type: 'input',
87+
name: 'name',
88+
message: `Enter the name of the ${name}:`,
89+
filter: nameParser,
90+
validate: inputIsRequired
91+
}
92+
]);
93+
const amount = context.name.split('/').length - 1;
94+
context.deepness = '';
95+
_.times(amount, () => context.deepness += '../');
96+
} else {
97+
context.name = nameParser(context.name);
98+
}
99+
return context;
100+
}
101+
63102
}
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,14 @@ import { config } from 'dotenv';
2222
config();
2323

2424
// Configures the logger
25-
import { LoggerConfig } from '../config/LoggerConfig';
25+
import { LoggerConfig } from '../../config/LoggerConfig';
2626
new LoggerConfig().configure();
2727

2828
figlet('console', (error: any, data: any) => {
2929
console.log(chalk.blue(data));
30-
console.log(chalk.green('➜ ') + chalk.bold(process.argv[2]));
31-
console.log();
3230

3331
// Find all command files
34-
glob(path.join(__dirname, '**/*Command.ts'), (err: any, matches: string[]) => {
32+
glob(path.join(__dirname, '../**/*Command.ts'), (err: any, matches: string[]) => {
3533
if (err) {
3634
console.log(err);
3735
return;
@@ -41,10 +39,21 @@ figlet('console', (error: any, data: any) => {
4139
.filter(m => m.search(/\/lib/g) <= 0)
4240
.map(m => ({
4341
path: m,
44-
name: m.replace((isWindows() ? __dirname.replace(/\\/g, '/') : __dirname), '').replace('.ts', '').substring(1)
42+
name: m.replace((isWindows() ? __dirname.replace(/\\/g, '/') : __dirname).replace('/lib', ''), '').replace('.ts', '').substring(1)
4543
}));
4644

4745
const commands = files.map(f => require(f.path)[f.name]);
46+
const keys = commands.map(c => c.command);
47+
const key = process.argv[2];
48+
49+
if (keys.indexOf(key) < 0) {
50+
console.log(chalk.red('➜ ') + chalk.bold(`Command ${key} was not found!`));
51+
console.log();
52+
return;
53+
}
54+
55+
console.log(chalk.green('➜ ') + chalk.bold(key));
56+
console.log();
4857

4958
commands.forEach((c) => {
5059
commander

0 commit comments

Comments
 (0)