Skip to content

Commit f80ee70

Browse files
authored
chore: migrate code to ESM and add tests (#17)
1 parent c7a9715 commit f80ee70

20 files changed

+1248
-501
lines changed

.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
MIKRO_ORM_DYNAMIC_IMPORTS=1
2+
MIKRO_ORM_TYPE=mongo
3+
MIKRO_ORM_ENTITIES=./app/entities/*.js
4+
MIKRO_ORM_DB_NAME=mikro-orm-express-js
5+
MIKRO_ORM_DEBUG=true

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [B4nan]
2+
custom: 'https://paypal.me/mikroorm'

.github/workflows/tests.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: tests
2+
3+
on:
4+
push:
5+
branches: [ master, renovate/** ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
test:
11+
name: Tests
12+
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
node-version: [ 14, 16 ]
18+
steps:
19+
- name: Checkout Source code
20+
uses: actions/checkout@v2
21+
22+
- name: Use Node.js ${{ matrix.node-version }}
23+
uses: actions/setup-node@v2
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
27+
- name: Cache node_modules
28+
uses: actions/cache@v2
29+
with:
30+
path: '**/node_modules'
31+
key: ${{ runner.os }}-${{ matrix.node-version }}-modules-${{ hashFiles('**/yarn.lock') }}
32+
33+
- name: Init docker
34+
run: docker-compose up -d
35+
36+
- name: Install
37+
run: yarn
38+
39+
- name: Test
40+
run: yarn test
41+
42+
- name: Teardown docker
43+
run: docker-compose down

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
temp
22
node_modules
3+
.idea

app/controllers/author.controller.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
'use strict';
22

3-
const { QueryOrder } = require('@mikro-orm/core');
4-
const { Router } = require('express');
5-
const server = require('../server');
6-
const { Author } = require('../entities/Author');
3+
import { QueryOrder, wrap } from '@mikro-orm/core';
4+
import { Router } from 'express';
5+
import { DI } from '../server.js';
6+
import { Author } from '../entities/Author.js';
77

88
const router = Router();
99

1010
router.get('/', async (req, res) => {
11-
const authors = await server.DI.authorRepository.findAll(['books'], { name: QueryOrder.DESC }, 20);
11+
const authors = await DI.authorRepository.findAll({
12+
populate: ['books'],
13+
orderBy: { name: QueryOrder.DESC },
14+
limit: 20,
15+
});
1216
res.json(authors);
1317
});
1418

1519
router.get('/:id', async (req, res) => {
1620
try {
17-
const author = await server.DI.authorRepository.findOne(req.params.id, ['books']);
21+
const author = await DI.authorRepository.findOne(req.params.id, {
22+
populate: ['books'],
23+
});
1824

1925
if (!author) {
2026
return res.status(404).json({ message: 'Author not found' });
@@ -33,9 +39,8 @@ router.post('/', async (req, res) => {
3339
}
3440

3541
try {
36-
const author = new Author(req.body.name, req.body.email);
37-
author.assign(req.body);
38-
await server.DI.authorRepository.persist(author).flush();
42+
const author = DI.em.create(Author, req.body);
43+
await DI.em.persist(author).flush();
3944

4045
res.json(author);
4146
} catch (e) {
@@ -45,19 +50,14 @@ router.post('/', async (req, res) => {
4550

4651
router.put('/:id', async (req, res) => {
4752
try {
48-
const author = await server.DI.authorRepository.findOne(+req.params.id);
49-
50-
if (!author) {
51-
return res.status(404).json({ message: 'Author not found' });
52-
}
53-
54-
author.assign(req.body);
55-
await server.DI.authorRepository.flush();
53+
const author = await DI.authorRepository.findOneOrFail(req.params.id);
54+
wrap(author).assign(req.body);
55+
await DI.authorRepository.flush();
5656

5757
res.json(author);
5858
} catch (e) {
5959
return res.status(400).json({ message: e.message });
6060
}
6161
});
6262

63-
module.exports.AuthorController = router;
63+
export { router as AuthorController };
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import request from 'supertest';
2+
import expect from 'expect';
3+
import { app, DI, server } from '../server.js';
4+
5+
describe('author controller', () => {
6+
7+
before(async () => {
8+
DI.orm.config.set('dbName', 'express-test-db');
9+
DI.orm.config.getLogger().setDebugMode(false);
10+
await DI.orm.config.getDriver().reconnect();
11+
await DI.orm.getSchemaGenerator().clearDatabase();
12+
});
13+
14+
after(async () => {
15+
await DI.orm.close(true);
16+
server.close();
17+
});
18+
19+
it(`CRUD`, async () => {
20+
let id;
21+
22+
await request(app)
23+
.post('/author')
24+
.send({ name: 'a1', email: 'e1', books: [{ title: 'b1' }, { title: 'b2' }] })
25+
.then(res => {
26+
expect(res.status).toBe(200);
27+
expect(res.body.id).toBeDefined();
28+
expect(res.body.name).toBe('a1');
29+
expect(res.body.email).toBe('e1');
30+
expect(res.body.termsAccepted).toBe(false);
31+
expect(res.body.books).toHaveLength(2);
32+
33+
id = res.body.id;
34+
});
35+
36+
await request(app)
37+
.get('/author')
38+
.then(res => {
39+
expect(res.status).toBe(200);
40+
expect(res.body).toHaveLength(1);
41+
expect(res.body[0].id).toBeDefined();
42+
expect(res.body[0].name).toBe('a1');
43+
expect(res.body[0].email).toBe('e1');
44+
expect(res.body[0].termsAccepted).toBe(false);
45+
expect(res.body[0].books).toHaveLength(2);
46+
});
47+
48+
await request(app)
49+
.put('/author/' + id)
50+
.send({ name: 'a2' })
51+
.then(res => {
52+
expect(res.status).toBe(200);
53+
expect(res.body.id).toBeDefined();
54+
expect(res.body.name).toBe('a2');
55+
expect(res.body.email).toBe('e1');
56+
expect(res.body.termsAccepted).toBe(false);
57+
});
58+
});
59+
60+
});

app/controllers/book.controller.js

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
'use strict';
22

3-
const { QueryOrder } = require('@mikro-orm/core');
4-
const { Router } = require('express');
5-
const server = require('../server');
6-
const { Book } = require('../entities');
3+
import { QueryOrder, wrap } from '@mikro-orm/core';
4+
import { Router } from 'express';
5+
import { Book } from '../entities/index.js';
6+
import { DI } from '../server.js';
77

88
const router = Router();
99

1010
router.get('/', async (req, res) => {
11-
const books = await server.DI.bookRepository.findAll(['author'], { title: QueryOrder.DESC }, 20);
11+
const books = await DI.bookRepository.findAll({
12+
populate: ['author'],
13+
orderBy: { title: QueryOrder.DESC },
14+
limit: 20,
15+
});
1216
res.json(books);
1317
});
1418

1519
router.get('/:id', async (req, res) => {
1620
try {
17-
const book = await server.DI.bookRepository.findOne(+req.params.id, ['author']);
18-
19-
if (!book) {
20-
return res.status(404).json({ message: 'Book not found' });
21-
}
22-
21+
const book = await DI.bookRepository.findOneOrFail(req.params.id, {
22+
populate: ['author'],
23+
});
2324
res.json(book);
2425
} catch (e) {
2526
return res.status(400).json({ message: e.message });
@@ -33,9 +34,9 @@ router.post('/', async (req, res) => {
3334
}
3435

3536
try {
36-
const book = new Book(req.body.title, req.body.author);
37-
book.assign(req.body);
38-
await server.DI.bookRepository.persist(book).flush();
37+
const book = DI.em.create(Book, req.body);
38+
wrap(book.author, true).__initialized = true;
39+
await DI.em.persist(book).flush();
3940

4041
res.json(book);
4142
} catch (e) {
@@ -45,19 +46,14 @@ router.post('/', async (req, res) => {
4546

4647
router.put('/:id', async (req, res) => {
4748
try {
48-
const book = await server.DI.bookRepository.findOne(+req.params.id);
49-
50-
if (!book) {
51-
return res.status(404).json({ message: 'Book not found' });
52-
}
53-
54-
book.assign(req.body);
55-
await server.DI.bookRepository.flush();
49+
const book = await DI.bookRepository.findOneOrFail(req.params.id);
50+
wrap(book).assign(req.body);
51+
await DI.bookRepository.flush();
5652

5753
res.json(book);
5854
} catch (e) {
5955
return res.status(400).json({ message: e.message });
6056
}
6157
});
6258

63-
module.exports.BookController = router;
59+
export { router as BookController };
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import request from 'supertest';
2+
import expect from 'expect';
3+
import { app, DI, server } from '../server.js';
4+
5+
describe('author controller', () => {
6+
7+
before(async () => {
8+
DI.orm.config.set('dbName', 'express-test-db');
9+
DI.orm.config.getLogger().setDebugMode(false);
10+
await DI.orm.config.getDriver().reconnect();
11+
await DI.orm.getSchemaGenerator().clearDatabase();
12+
});
13+
14+
after(async () => {
15+
await DI.orm.close(true);
16+
server.close();
17+
});
18+
19+
it(`CRUD`, async () => {
20+
let id;
21+
22+
await request(app)
23+
.post('/book')
24+
.send({ title: 'b1', author: { name: 'a1', email: 'e1' } })
25+
.then(res => {
26+
expect(res.status).toBe(200);
27+
expect(res.body.id).toBeDefined();
28+
expect(res.body.title).toBe('b1');
29+
expect(res.body.author.name).toBe('a1');
30+
expect(res.body.author.email).toBe('e1');
31+
expect(res.body.author.termsAccepted).toBe(false);
32+
expect(res.body.author.books).toHaveLength(1);
33+
34+
id = res.body.id;
35+
});
36+
37+
await request(app)
38+
.get('/book')
39+
.then(res => {
40+
expect(res.status).toBe(200);
41+
expect(res.body[0].id).toBeDefined();
42+
expect(res.body[0].title).toBe('b1');
43+
expect(res.body[0].author.name).toBe('a1');
44+
expect(res.body[0].author.email).toBe('e1');
45+
expect(res.body[0].author.termsAccepted).toBe(false);
46+
});
47+
48+
await request(app)
49+
.put('/book/' + id)
50+
.send({ title: 'b2' })
51+
.then(res => {
52+
expect(res.status).toBe(200);
53+
expect(res.body.id).toBeDefined();
54+
expect(res.body.title).toBe('b2');
55+
expect(res.body.author).toBeDefined();
56+
});
57+
});
58+
59+
});

app/controllers/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
'use strict';
22

3-
module.exports.AuthorController = require('./author.controller').AuthorController;
4-
module.exports.BookController = require('./book.controller').BookController;
3+
export * from './author.controller.js';
4+
export * from './book.controller.js';

app/entities/Author.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use strict';
22

3-
const { Collection, EntitySchema } = require('@mikro-orm/core');
4-
const { Book } = require('./Book');
5-
const { BaseEntity } = require('./BaseEntity');
3+
import { Collection, EntitySchema } from '@mikro-orm/core';
4+
import { Book } from './Book.js';
5+
import { BaseEntity } from './BaseEntity.js';
66

77
/**
88
* @property {string} name
@@ -16,7 +16,7 @@ const { BaseEntity } = require('./BaseEntity');
1616
* @property {number} version
1717
* @property {string} versionAsString
1818
*/
19-
class Author extends BaseEntity {
19+
export class Author extends BaseEntity {
2020

2121
/**
2222
* @param {string} name
@@ -34,16 +34,16 @@ class Author extends BaseEntity {
3434
Author.beforeDestroyCalled = 0;
3535
Author.afterDestroyCalled = 0;
3636

37-
const schema = new EntitySchema({
37+
export const schema = new EntitySchema({
3838
class: Author,
3939
extends: 'BaseEntity',
4040
properties: {
4141
name: { type: 'string' },
4242
email: { type: 'string' },
43-
age: { type: 'number' },
43+
age: { type: 'number', nullable: true },
4444
termsAccepted: { type: 'boolean' },
45-
identities: { type: 'string[]' },
46-
born: { type: 'Date' },
45+
identities: { type: 'string[]', nullable: true },
46+
born: { type: 'Date', nullable: true },
4747
books: {
4848
reference: '1:m',
4949
mappedBy: 'author',
@@ -52,10 +52,7 @@ const schema = new EntitySchema({
5252
favouriteBook: {
5353
reference: 'm:1',
5454
type: 'Book',
55+
nullable: true,
5556
},
5657
},
5758
});
58-
59-
module.exports.Author = Author;
60-
module.exports.entity = Author;
61-
module.exports.schema = schema;

0 commit comments

Comments
 (0)