Skip to content

Commit 6d2f5cc

Browse files
author
hirsch88
committed
Merge branch 'feature/console_resource_add_tests' into develop
2 parents 870ea9a + 064a7bd commit 6d2f5cc

File tree

14 files changed

+336
-56
lines changed

14 files changed

+336
-56
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

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
}

src/console/lib/utils.ts

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,40 @@ export const removeSufix = (suffix: string, value: string) => {
2121
};
2222

2323
export const filterInput = (suffix: string, prefix = '') => (value: string) => {
24+
if (value.indexOf('/') < 0) {
25+
return value;
26+
}
2427
let vs = value.split('/');
2528
vs = vs.map((v) => _.camelCase(v));
2629
vs[vs.length - 1] = _.capitalize(vs[vs.length - 1]);
2730
return (vs.join('/')) + prefix + suffix;
2831
};
2932

30-
export const buildFilePath = (targetPath: string, fileName: string, extension = '.ts') =>
31-
path.join(__dirname, `/../../${targetPath}`, `${fileName}${extension}`);
32-
33-
export const inputIsRequired = (value: any) => !!value;
34-
35-
export const askFileName = async (context: any, name: string, suffix: string, prefix: string) => {
36-
if (context === undefined || context.name === undefined) {
37-
const prompt = inquirer.createPromptModule();
38-
context = await prompt([
39-
{
40-
type: 'input',
41-
name: 'name',
42-
message: `Enter the name of the ${name}:`,
43-
filter: filterInput(suffix, prefix),
44-
validate: inputIsRequired
45-
}
46-
]);
47-
const amount = context.name.split('/').length - 1;
48-
context.deepness = '';
49-
_.times(amount, () => context.deepness += '../');
33+
export const buildFilePath = (targetPath: string, fileName: string, isTest = false, extension = '.ts') => {
34+
if (isTest) {
35+
return path.join(__dirname, `/../../../test/${targetPath}`, `${fileName}.test${extension}`);
5036
} else {
51-
context.name = filterInput(suffix, prefix)(context.name);
37+
return path.join(__dirname, `/../../${targetPath}`, `${fileName}${extension}`);
5238
}
53-
return context;
5439
};
5540

56-
export const existsFile = async (path: string, stop: boolean = false) => {
41+
export const inputIsRequired = (value: any) => !!value;
42+
43+
export const existsFile = async (path: string, stop: boolean = false, isTest = false) => {
5744
const prompt = inquirer.createPromptModule();
5845
return new Promise((resolve, reject) => {
5946
fs.exists(path, async (exists) => {
6047

6148
if (exists) {
62-
const fileName = path.split('/src/')[1];
49+
let fileName = path.split('/src/')[1];
50+
if (isTest) {
51+
fileName = path.split('/test/')[1];
52+
}
6353
const answer = await prompt([
6454
{
6555
type: 'confirm',
6656
name: 'override',
67-
message: `Override "src/${fileName}"?`,
57+
message: `Override "${isTest ? 'test' : 'src'}/${fileName}"?`,
6858
default: true
6959
}
7060
]);

0 commit comments

Comments
 (0)