@loopback/example-references-many 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +5 -0
- package/.eslintrc.js +8 -0
- package/.prettierignore +2 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +20 -0
- package/.vscode/tasks.json +29 -0
- package/CHANGELOG.md +25 -0
- package/Dockerfile +28 -0
- package/LICENSE +25 -0
- package/README.md +49 -0
- package/data/db.json +18 -0
- package/dist/__tests__/acceptance/account.acceptance.d.ts +1 -0
- package/dist/__tests__/acceptance/account.acceptance.js +111 -0
- package/dist/__tests__/acceptance/account.acceptance.js.map +1 -0
- package/dist/__tests__/acceptance/customer.acceptance.d.ts +1 -0
- package/dist/__tests__/acceptance/customer.acceptance.js +163 -0
- package/dist/__tests__/acceptance/customer.acceptance.js.map +1 -0
- package/dist/__tests__/acceptance/home-page.acceptance.d.ts +1 -0
- package/dist/__tests__/acceptance/home-page.acceptance.js +31 -0
- package/dist/__tests__/acceptance/home-page.acceptance.js.map +1 -0
- package/dist/__tests__/acceptance/test-helper.d.ts +7 -0
- package/dist/__tests__/acceptance/test-helper.js +20 -0
- package/dist/__tests__/acceptance/test-helper.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +26 -0
- package/dist/__tests__/helpers.js +101 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/integration/customer.repository.integration.d.ts +1 -0
- package/dist/__tests__/integration/customer.repository.integration.js +61 -0
- package/dist/__tests__/integration/customer.repository.integration.js.map +1 -0
- package/dist/__tests__/unit/controllers/account.controller.unit.d.ts +1 -0
- package/dist/__tests__/unit/controllers/account.controller.unit.js +106 -0
- package/dist/__tests__/unit/controllers/account.controller.unit.js.map +1 -0
- package/dist/__tests__/unit/controllers/customer.controller.unit.d.ts +1 -0
- package/dist/__tests__/unit/controllers/customer.controller.unit.js +108 -0
- package/dist/__tests__/unit/controllers/customer.controller.unit.js.map +1 -0
- package/dist/application.d.ts +272 -0
- package/dist/application.js +41 -0
- package/dist/application.js.map +1 -0
- package/dist/controllers/account.controller.d.ts +15 -0
- package/dist/controllers/account.controller.js +192 -0
- package/dist/controllers/account.controller.js.map +1 -0
- package/dist/controllers/customer.controller.d.ts +15 -0
- package/dist/controllers/customer.controller.js +192 -0
- package/dist/controllers/customer.controller.js.map +1 -0
- package/dist/controllers/index.d.ts +2 -0
- package/dist/controllers/index.js +10 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/datasources/db.datasource.d.ts +12 -0
- package/dist/datasources/db.datasource.js +34 -0
- package/dist/datasources/db.datasource.js.map +1 -0
- package/dist/datasources/index.d.ts +1 -0
- package/dist/datasources/index.js +9 -0
- package/dist/datasources/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +1 -0
- package/dist/migrate.js +25 -0
- package/dist/migrate.js.map +1 -0
- package/dist/models/account.model.d.ts +9 -0
- package/dist/models/account.model.js +35 -0
- package/dist/models/account.model.js.map +1 -0
- package/dist/models/customer.model.d.ts +13 -0
- package/dist/models/customer.model.js +45 -0
- package/dist/models/customer.model.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +10 -0
- package/dist/models/index.js.map +1 -0
- package/dist/openapi-spec.d.ts +1 -0
- package/dist/openapi-spec.js +28 -0
- package/dist/openapi-spec.js.map +1 -0
- package/dist/repositories/account.repository.d.ts +6 -0
- package/dist/repositories/account.repository.js +23 -0
- package/dist/repositories/account.repository.js.map +1 -0
- package/dist/repositories/customer.repository.d.ts +11 -0
- package/dist/repositories/customer.repository.js +29 -0
- package/dist/repositories/customer.repository.js.map +1 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +10 -0
- package/dist/repositories/index.js.map +1 -0
- package/dist/sequence.d.ts +3 -0
- package/dist/sequence.js +12 -0
- package/dist/sequence.js.map +1 -0
- package/package.json +78 -0
- package/public/index.html +72 -0
- package/src/__tests__/acceptance/account.acceptance.ts +139 -0
- package/src/__tests__/acceptance/customer.acceptance.ts +198 -0
- package/src/__tests__/acceptance/home-page.acceptance.ts +36 -0
- package/src/__tests__/acceptance/test-helper.ts +29 -0
- package/src/__tests__/helpers.ts +119 -0
- package/src/__tests__/integration/customer.repository.integration.ts +80 -0
- package/src/__tests__/unit/controllers/account.controller.unit.ts +127 -0
- package/src/__tests__/unit/controllers/customer.controller.unit.ts +136 -0
- package/src/application.ts +49 -0
- package/src/controllers/account.controller.ts +177 -0
- package/src/controllers/customer.controller.ts +177 -0
- package/src/controllers/index.ts +7 -0
- package/src/datasources/db.datasource.ts +34 -0
- package/src/datasources/index.ts +6 -0
- package/src/index.ts +42 -0
- package/src/migrate.ts +25 -0
- package/src/models/account.model.ts +31 -0
- package/src/models/customer.model.ts +40 -0
- package/src/models/index.ts +7 -0
- package/src/openapi-spec.ts +28 -0
- package/src/repositories/account.repository.ts +19 -0
- package/src/repositories/customer.repository.ts +42 -0
- package/src/repositories/index.ts +7 -0
- package/src/sequence.ts +8 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-references-many
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {juggler} from '@loopback/repository';
|
|
7
|
+
import {givenHttpServerConfig} from '@loopback/testlab';
|
|
8
|
+
import {ReferencesManyApplication} from '../application';
|
|
9
|
+
import {Account, Customer} from '../models';
|
|
10
|
+
import {AccountRepository, CustomerRepository} from '../repositories';
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
==============================================================================
|
|
14
|
+
HELPER FUNCTIONS
|
|
15
|
+
If you find yourself creating the same helper functions across different
|
|
16
|
+
test files, then extracting those functions into helper modules is an easy
|
|
17
|
+
way to reduce duplication.
|
|
18
|
+
|
|
19
|
+
Other tips:
|
|
20
|
+
|
|
21
|
+
- Using the super awesome Partial<T> type in conjunction with Object.assign
|
|
22
|
+
means you can:
|
|
23
|
+
* customize the object you get back based only on what's important
|
|
24
|
+
to you during a particular test
|
|
25
|
+
* avoid writing test logic that is brittle with respect to the properties
|
|
26
|
+
of your object
|
|
27
|
+
- Making the input itself optional means you don't need to do anything special
|
|
28
|
+
for tests where the particular details of the input don't matter.
|
|
29
|
+
==============================================================================
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate a complete Customer object for use with tests.
|
|
34
|
+
* @param customer - A partial (or complete) Customer object.
|
|
35
|
+
*/
|
|
36
|
+
export function givenCustomer(customer?: Partial<Customer>) {
|
|
37
|
+
const data = Object.assign(
|
|
38
|
+
{
|
|
39
|
+
firstName: 'John',
|
|
40
|
+
lastName: 'Doe',
|
|
41
|
+
},
|
|
42
|
+
customer,
|
|
43
|
+
);
|
|
44
|
+
return new Customer(data);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generate a complete Account object for use with tests.
|
|
49
|
+
* @param account - A partial (or complete) Account object.
|
|
50
|
+
*/
|
|
51
|
+
export function givenAccount(account?: Partial<Account>) {
|
|
52
|
+
const data = Object.assign({balance: 999}, account);
|
|
53
|
+
return new Account(data);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function givenRunningApplicationWithCustomConfiguration() {
|
|
57
|
+
const app = new ReferencesManyApplication({
|
|
58
|
+
rest: givenHttpServerConfig(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await app.boot();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Override default config for DataSource for testing so we don't write
|
|
65
|
+
* test data to file when using the memory connector.
|
|
66
|
+
*/
|
|
67
|
+
app.bind('datasources.config.db').to({
|
|
68
|
+
name: 'db',
|
|
69
|
+
connector: 'memory',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Start Application
|
|
73
|
+
await app.start();
|
|
74
|
+
return app;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function givenCustomerRepositories(
|
|
78
|
+
app: ReferencesManyApplication,
|
|
79
|
+
) {
|
|
80
|
+
const customerRepo = await app.getRepository(CustomerRepository);
|
|
81
|
+
const accountRepo = await app.getRepository(AccountRepository);
|
|
82
|
+
return {customerRepo, accountRepo};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function givenAccountRepositories(app: ReferencesManyApplication) {
|
|
86
|
+
const accountRepo = await app.getRepository(AccountRepository);
|
|
87
|
+
return {accountRepo};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function givenCustomerInstance(
|
|
91
|
+
customerRepo: CustomerRepository,
|
|
92
|
+
customer?: Partial<Customer>,
|
|
93
|
+
) {
|
|
94
|
+
return customerRepo.create(givenCustomer(customer));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function givenAccountInstance(
|
|
98
|
+
accountRepo: AccountRepository,
|
|
99
|
+
account?: Partial<Account>,
|
|
100
|
+
) {
|
|
101
|
+
return accountRepo.create(givenAccount(account));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function givenEmptyDatabase() {
|
|
105
|
+
const accountRepo: AccountRepository = new AccountRepository(testdb);
|
|
106
|
+
const customerRepo: CustomerRepository = new CustomerRepository(
|
|
107
|
+
testdb,
|
|
108
|
+
async () => accountRepo,
|
|
109
|
+
async () => customerRepo,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
await accountRepo.deleteAll();
|
|
113
|
+
await customerRepo.deleteAll();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const testdb: juggler.DataSource = new juggler.DataSource({
|
|
117
|
+
name: 'db',
|
|
118
|
+
connector: 'memory',
|
|
119
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-references-many
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {expect, toJSON} from '@loopback/testlab';
|
|
7
|
+
import {AccountRepository, CustomerRepository} from '../../repositories';
|
|
8
|
+
import {
|
|
9
|
+
givenAccountInstance,
|
|
10
|
+
givenCustomerInstance,
|
|
11
|
+
givenEmptyDatabase,
|
|
12
|
+
testdb,
|
|
13
|
+
} from '../helpers';
|
|
14
|
+
|
|
15
|
+
describe('ReferencesManyRepository', () => {
|
|
16
|
+
let accountRepo: AccountRepository;
|
|
17
|
+
let customerRepo: CustomerRepository;
|
|
18
|
+
|
|
19
|
+
before(async () => {
|
|
20
|
+
accountRepo = new AccountRepository(testdb);
|
|
21
|
+
customerRepo = new CustomerRepository(
|
|
22
|
+
testdb,
|
|
23
|
+
async () => accountRepo,
|
|
24
|
+
async () => customerRepo,
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
beforeEach(givenEmptyDatabase);
|
|
29
|
+
|
|
30
|
+
it('includes Accounts in find method result', async () => {
|
|
31
|
+
const account = await givenAccountInstance(accountRepo);
|
|
32
|
+
const customer = await givenCustomerInstance(customerRepo, {
|
|
33
|
+
accountIds: [account.id],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const response = await customerRepo.find({
|
|
37
|
+
include: ['accounts'],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(toJSON(response)).to.deepEqual([
|
|
41
|
+
{
|
|
42
|
+
...toJSON(customer),
|
|
43
|
+
accounts: [toJSON(account)],
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('includes Accounts in findById result', async () => {
|
|
49
|
+
const account = await givenAccountInstance(accountRepo);
|
|
50
|
+
const customer = await givenCustomerInstance(customerRepo, {
|
|
51
|
+
accountIds: [account.id],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const response = await customerRepo.findById(customer.id, {
|
|
55
|
+
include: ['accounts'],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(toJSON(response)).to.deepEqual({
|
|
59
|
+
...toJSON(customer),
|
|
60
|
+
accounts: [toJSON(account)],
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('includes Accounts in findOne method result', async () => {
|
|
65
|
+
const account = await givenAccountInstance(accountRepo);
|
|
66
|
+
const customer = await givenCustomerInstance(customerRepo, {
|
|
67
|
+
accountIds: [account.id],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const response = await customerRepo.findOne({
|
|
71
|
+
where: {id: customer.id},
|
|
72
|
+
include: ['accounts'],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(toJSON(response)).to.deepEqual({
|
|
76
|
+
...toJSON(customer),
|
|
77
|
+
accounts: [toJSON(account)],
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-account-list
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
createStubInstance,
|
|
8
|
+
expect,
|
|
9
|
+
sinon,
|
|
10
|
+
StubbedInstanceWithSinonAccessor,
|
|
11
|
+
} from '@loopback/testlab';
|
|
12
|
+
import {AccountController} from '../../../controllers';
|
|
13
|
+
import {Account} from '../../../models';
|
|
14
|
+
import {AccountRepository} from '../../../repositories';
|
|
15
|
+
import {givenAccount} from '../../helpers';
|
|
16
|
+
|
|
17
|
+
describe('AccountController', () => {
|
|
18
|
+
let accountRepo: StubbedInstanceWithSinonAccessor<AccountRepository>;
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
=============================================================================
|
|
22
|
+
TEST VARIABLES
|
|
23
|
+
Combining top-level objects with our resetRepositories method means we don't
|
|
24
|
+
need to duplicate several variable assignments (and generation statements)
|
|
25
|
+
in all of our test logic.
|
|
26
|
+
|
|
27
|
+
NOTE: If you wanted to parallelize your test runs, you should avoid this
|
|
28
|
+
pattern since each of these tests is sharing references.
|
|
29
|
+
=============================================================================
|
|
30
|
+
*/
|
|
31
|
+
let controller: AccountController;
|
|
32
|
+
let aAccount: Account;
|
|
33
|
+
let aAccountWithId: Account;
|
|
34
|
+
let aChangedAccount: Account;
|
|
35
|
+
let aListOfAccounts: Account[];
|
|
36
|
+
|
|
37
|
+
beforeEach(resetRepositories);
|
|
38
|
+
|
|
39
|
+
describe('createAccount', () => {
|
|
40
|
+
it('creates a Account', async () => {
|
|
41
|
+
const create = accountRepo.stubs.create;
|
|
42
|
+
create.resolves(aAccountWithId);
|
|
43
|
+
const result = await controller.create(aAccount);
|
|
44
|
+
expect(result).to.eql(aAccountWithId);
|
|
45
|
+
sinon.assert.calledWith(create, aAccount);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('findAccountById', () => {
|
|
50
|
+
it('returns a account if it exists', async () => {
|
|
51
|
+
const findById = accountRepo.stubs.findById;
|
|
52
|
+
findById.resolves(aAccountWithId);
|
|
53
|
+
expect(await controller.findById(aAccountWithId.id as number)).to.eql(
|
|
54
|
+
aAccountWithId,
|
|
55
|
+
);
|
|
56
|
+
sinon.assert.calledWith(findById, aAccountWithId.id);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('findAccounts', () => {
|
|
61
|
+
it('returns multiple accounts if they exist', async () => {
|
|
62
|
+
const find = accountRepo.stubs.find;
|
|
63
|
+
find.resolves(aListOfAccounts);
|
|
64
|
+
expect(await controller.find()).to.eql(aListOfAccounts);
|
|
65
|
+
sinon.assert.called(find);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns empty list if no accounts exist', async () => {
|
|
69
|
+
const find = accountRepo.stubs.find;
|
|
70
|
+
const expected: Account[] = [];
|
|
71
|
+
find.resolves(expected);
|
|
72
|
+
expect(await controller.find()).to.eql(expected);
|
|
73
|
+
sinon.assert.called(find);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('replaceAccount', () => {
|
|
78
|
+
it('successfully replaces existing items', async () => {
|
|
79
|
+
const replaceById = accountRepo.stubs.replaceById;
|
|
80
|
+
replaceById.resolves();
|
|
81
|
+
await controller.replaceById(
|
|
82
|
+
aAccountWithId.id as number,
|
|
83
|
+
aChangedAccount,
|
|
84
|
+
);
|
|
85
|
+
sinon.assert.calledWith(replaceById, aAccountWithId.id, aChangedAccount);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('updateAccount', () => {
|
|
90
|
+
it('successfully updates existing items', async () => {
|
|
91
|
+
const updateById = accountRepo.stubs.updateById;
|
|
92
|
+
updateById.resolves();
|
|
93
|
+
await controller.updateById(aAccountWithId.id as number, aChangedAccount);
|
|
94
|
+
sinon.assert.calledWith(updateById, aAccountWithId.id, aChangedAccount);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('deleteAccount', () => {
|
|
99
|
+
it('successfully deletes existing items', async () => {
|
|
100
|
+
const deleteById = accountRepo.stubs.deleteById;
|
|
101
|
+
deleteById.resolves();
|
|
102
|
+
await controller.deleteById(aAccountWithId.id as number);
|
|
103
|
+
sinon.assert.calledWith(deleteById, aAccountWithId.id);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
function resetRepositories() {
|
|
108
|
+
accountRepo = createStubInstance(AccountRepository);
|
|
109
|
+
aAccount = givenAccount();
|
|
110
|
+
aAccountWithId = givenAccount({
|
|
111
|
+
id: 1,
|
|
112
|
+
});
|
|
113
|
+
aListOfAccounts = [
|
|
114
|
+
aAccountWithId,
|
|
115
|
+
givenAccount({
|
|
116
|
+
id: 2,
|
|
117
|
+
balance: 5,
|
|
118
|
+
}),
|
|
119
|
+
] as Account[];
|
|
120
|
+
aChangedAccount = givenAccount({
|
|
121
|
+
id: aAccountWithId.id,
|
|
122
|
+
balance: 10,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
controller = new AccountController(accountRepo);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-customer-list
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
createStubInstance,
|
|
8
|
+
expect,
|
|
9
|
+
sinon,
|
|
10
|
+
StubbedInstanceWithSinonAccessor,
|
|
11
|
+
} from '@loopback/testlab';
|
|
12
|
+
import {CustomerController} from '../../../controllers';
|
|
13
|
+
import {Customer} from '../../../models';
|
|
14
|
+
import {CustomerRepository} from '../../../repositories';
|
|
15
|
+
import {givenCustomer} from '../../helpers';
|
|
16
|
+
|
|
17
|
+
describe('CustomerController', () => {
|
|
18
|
+
let customerRepo: StubbedInstanceWithSinonAccessor<CustomerRepository>;
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
=============================================================================
|
|
22
|
+
TEST VARIABLES
|
|
23
|
+
Combining top-level objects with our resetRepositories method means we don't
|
|
24
|
+
need to duplicate several variable assignments (and generation statements)
|
|
25
|
+
in all of our test logic.
|
|
26
|
+
|
|
27
|
+
NOTE: If you wanted to parallelize your test runs, you should avoid this
|
|
28
|
+
pattern since each of these tests is sharing references.
|
|
29
|
+
=============================================================================
|
|
30
|
+
*/
|
|
31
|
+
let controller: CustomerController;
|
|
32
|
+
let aCustomer: Customer;
|
|
33
|
+
let aCustomerWithId: Customer;
|
|
34
|
+
let aChangedCustomer: Customer;
|
|
35
|
+
let aListOfCustomers: Customer[];
|
|
36
|
+
|
|
37
|
+
beforeEach(resetRepositories);
|
|
38
|
+
|
|
39
|
+
describe('createCustomer', () => {
|
|
40
|
+
it('creates a Customer', async () => {
|
|
41
|
+
const create = customerRepo.stubs.create;
|
|
42
|
+
create.resolves(aCustomerWithId);
|
|
43
|
+
const result = await controller.create(aCustomer);
|
|
44
|
+
expect(result).to.eql(aCustomerWithId);
|
|
45
|
+
sinon.assert.calledWith(create, aCustomer);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('findCustomerById', () => {
|
|
50
|
+
it('returns a customer if it exists', async () => {
|
|
51
|
+
const findById = customerRepo.stubs.findById;
|
|
52
|
+
findById.resolves(aCustomerWithId);
|
|
53
|
+
expect(await controller.findById(aCustomerWithId.id as number)).to.eql(
|
|
54
|
+
aCustomerWithId,
|
|
55
|
+
);
|
|
56
|
+
sinon.assert.calledWith(findById, aCustomerWithId.id);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('findCustomers', () => {
|
|
61
|
+
it('returns multiple customers if they exist', async () => {
|
|
62
|
+
const find = customerRepo.stubs.find;
|
|
63
|
+
find.resolves(aListOfCustomers);
|
|
64
|
+
expect(await controller.find()).to.eql(aListOfCustomers);
|
|
65
|
+
sinon.assert.called(find);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns empty list if no customers exist', async () => {
|
|
69
|
+
const find = customerRepo.stubs.find;
|
|
70
|
+
const expected: Customer[] = [];
|
|
71
|
+
find.resolves(expected);
|
|
72
|
+
expect(await controller.find()).to.eql(expected);
|
|
73
|
+
sinon.assert.called(find);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('replaceCustomer', () => {
|
|
78
|
+
it('successfully replaces existing items', async () => {
|
|
79
|
+
const replaceById = customerRepo.stubs.replaceById;
|
|
80
|
+
replaceById.resolves();
|
|
81
|
+
await controller.replaceById(
|
|
82
|
+
aCustomerWithId.id as number,
|
|
83
|
+
aChangedCustomer,
|
|
84
|
+
);
|
|
85
|
+
sinon.assert.calledWith(
|
|
86
|
+
replaceById,
|
|
87
|
+
aCustomerWithId.id,
|
|
88
|
+
aChangedCustomer,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('updateCustomer', () => {
|
|
94
|
+
it('successfully updates existing items', async () => {
|
|
95
|
+
const updateById = customerRepo.stubs.updateById;
|
|
96
|
+
updateById.resolves();
|
|
97
|
+
await controller.updateById(
|
|
98
|
+
aCustomerWithId.id as number,
|
|
99
|
+
aChangedCustomer,
|
|
100
|
+
);
|
|
101
|
+
sinon.assert.calledWith(updateById, aCustomerWithId.id, aChangedCustomer);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('deleteCustomer', () => {
|
|
106
|
+
it('successfully deletes existing items', async () => {
|
|
107
|
+
const deleteById = customerRepo.stubs.deleteById;
|
|
108
|
+
deleteById.resolves();
|
|
109
|
+
await controller.deleteById(aCustomerWithId.id as number);
|
|
110
|
+
sinon.assert.calledWith(deleteById, aCustomerWithId.id);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
function resetRepositories() {
|
|
115
|
+
customerRepo = createStubInstance(CustomerRepository);
|
|
116
|
+
aCustomer = givenCustomer();
|
|
117
|
+
aCustomerWithId = givenCustomer({
|
|
118
|
+
id: 1,
|
|
119
|
+
});
|
|
120
|
+
aListOfCustomers = [
|
|
121
|
+
aCustomerWithId,
|
|
122
|
+
givenCustomer({
|
|
123
|
+
id: 2,
|
|
124
|
+
firstName: 'Dave',
|
|
125
|
+
lastName: 'Brubeck',
|
|
126
|
+
}),
|
|
127
|
+
] as Customer[];
|
|
128
|
+
aChangedCustomer = givenCustomer({
|
|
129
|
+
id: aCustomerWithId.id,
|
|
130
|
+
firstName: 'Tim',
|
|
131
|
+
lastName: 'Benton',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
controller = new CustomerController(customerRepo);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2022. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-references-many
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {BootMixin} from '@loopback/boot';
|
|
7
|
+
import {ApplicationConfig} from '@loopback/core';
|
|
8
|
+
import {RepositoryMixin} from '@loopback/repository';
|
|
9
|
+
import {RestApplication} from '@loopback/rest';
|
|
10
|
+
import {
|
|
11
|
+
RestExplorerBindings,
|
|
12
|
+
RestExplorerComponent,
|
|
13
|
+
} from '@loopback/rest-explorer';
|
|
14
|
+
import {ServiceMixin} from '@loopback/service-proxy';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import {MySequence} from './sequence';
|
|
17
|
+
|
|
18
|
+
export {ApplicationConfig};
|
|
19
|
+
|
|
20
|
+
export class ReferencesManyApplication extends BootMixin(
|
|
21
|
+
ServiceMixin(RepositoryMixin(RestApplication)),
|
|
22
|
+
) {
|
|
23
|
+
constructor(options: ApplicationConfig = {}) {
|
|
24
|
+
super(options);
|
|
25
|
+
|
|
26
|
+
// Set up the custom sequence
|
|
27
|
+
this.sequence(MySequence);
|
|
28
|
+
|
|
29
|
+
// Set up default home page
|
|
30
|
+
this.static('/', path.join(__dirname, '../public'));
|
|
31
|
+
|
|
32
|
+
// Customize @loopback/rest-explorer configuration here
|
|
33
|
+
this.configure(RestExplorerBindings.COMPONENT).to({
|
|
34
|
+
path: '/explorer',
|
|
35
|
+
});
|
|
36
|
+
this.component(RestExplorerComponent);
|
|
37
|
+
|
|
38
|
+
this.projectRoot = __dirname;
|
|
39
|
+
// Customize @loopback/boot Booter Conventions here
|
|
40
|
+
this.bootOptions = {
|
|
41
|
+
controllers: {
|
|
42
|
+
// Customize ControllerBooter Conventions here
|
|
43
|
+
dirs: ['controllers'],
|
|
44
|
+
extensions: ['.controller.js'],
|
|
45
|
+
nested: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2022. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/example-references-many
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Count,
|
|
8
|
+
CountSchema,
|
|
9
|
+
Filter,
|
|
10
|
+
FilterExcludingWhere,
|
|
11
|
+
repository,
|
|
12
|
+
Where,
|
|
13
|
+
} from '@loopback/repository';
|
|
14
|
+
import {
|
|
15
|
+
del,
|
|
16
|
+
get,
|
|
17
|
+
getModelSchemaRef,
|
|
18
|
+
param,
|
|
19
|
+
patch,
|
|
20
|
+
post,
|
|
21
|
+
put,
|
|
22
|
+
requestBody,
|
|
23
|
+
} from '@loopback/rest';
|
|
24
|
+
import {Account} from '../models';
|
|
25
|
+
import {AccountRepository} from '../repositories';
|
|
26
|
+
|
|
27
|
+
export class AccountController {
|
|
28
|
+
constructor(
|
|
29
|
+
@repository(AccountRepository)
|
|
30
|
+
public accountRepository: AccountRepository,
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
@post('/accounts', {
|
|
34
|
+
responses: {
|
|
35
|
+
'200': {
|
|
36
|
+
description: 'Account model instance',
|
|
37
|
+
content: {'application/json': {schema: getModelSchemaRef(Account)}},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
async create(
|
|
42
|
+
@requestBody({
|
|
43
|
+
content: {
|
|
44
|
+
'application/json': {
|
|
45
|
+
schema: getModelSchemaRef(Account, {
|
|
46
|
+
title: 'NewAccount',
|
|
47
|
+
exclude: ['id'],
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
account: Omit<Account, 'id'>,
|
|
53
|
+
): Promise<Account> {
|
|
54
|
+
return this.accountRepository.create(account);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@get('/accounts/count', {
|
|
58
|
+
responses: {
|
|
59
|
+
'200': {
|
|
60
|
+
description: 'Account model count',
|
|
61
|
+
content: {'application/json': {schema: CountSchema}},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
async count(@param.where(Account) where?: Where<Account>): Promise<Count> {
|
|
66
|
+
return this.accountRepository.count(where);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@get('/accounts', {
|
|
70
|
+
responses: {
|
|
71
|
+
'200': {
|
|
72
|
+
description: 'Array of Account model instances',
|
|
73
|
+
content: {
|
|
74
|
+
'application/json': {
|
|
75
|
+
schema: {
|
|
76
|
+
type: 'array',
|
|
77
|
+
items: getModelSchemaRef(Account, {includeRelations: true}),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
async find(
|
|
85
|
+
@param.filter(Account) filter?: Filter<Account>,
|
|
86
|
+
): Promise<Account[]> {
|
|
87
|
+
return this.accountRepository.find(filter);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@patch('/accounts', {
|
|
91
|
+
responses: {
|
|
92
|
+
'200': {
|
|
93
|
+
description: 'Account PATCH success count',
|
|
94
|
+
content: {'application/json': {schema: CountSchema}},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
async updateAll(
|
|
99
|
+
@requestBody({
|
|
100
|
+
content: {
|
|
101
|
+
'application/json': {
|
|
102
|
+
schema: getModelSchemaRef(Account, {partial: true}),
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
account: Account,
|
|
107
|
+
@param.where(Account) where?: Where<Account>,
|
|
108
|
+
): Promise<Count> {
|
|
109
|
+
return this.accountRepository.updateAll(account, where);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@get('/accounts/{id}', {
|
|
113
|
+
responses: {
|
|
114
|
+
'200': {
|
|
115
|
+
description: 'Account model instance',
|
|
116
|
+
content: {
|
|
117
|
+
'application/json': {
|
|
118
|
+
schema: getModelSchemaRef(Account, {includeRelations: true}),
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
async findById(
|
|
125
|
+
@param.path.number('id') id: number,
|
|
126
|
+
@param.filter(Account, {exclude: 'where'})
|
|
127
|
+
filter?: FilterExcludingWhere<Account>,
|
|
128
|
+
): Promise<Account> {
|
|
129
|
+
return this.accountRepository.findById(id, filter);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@patch('/accounts/{id}', {
|
|
133
|
+
responses: {
|
|
134
|
+
'204': {
|
|
135
|
+
description: 'Account PATCH success',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
async updateById(
|
|
140
|
+
@param.path.number('id') id: number,
|
|
141
|
+
@requestBody({
|
|
142
|
+
content: {
|
|
143
|
+
'application/json': {
|
|
144
|
+
schema: getModelSchemaRef(Account, {partial: true}),
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
account: Account,
|
|
149
|
+
): Promise<void> {
|
|
150
|
+
await this.accountRepository.updateById(id, account);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@del('/accounts/{id}', {
|
|
154
|
+
responses: {
|
|
155
|
+
'204': {
|
|
156
|
+
description: 'Account DELETE success',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
async deleteById(@param.path.number('id') id: number): Promise<void> {
|
|
161
|
+
await this.accountRepository.deleteById(id);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@put('/accounts/{id}', {
|
|
165
|
+
responses: {
|
|
166
|
+
'204': {
|
|
167
|
+
description: 'Account PUT success',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
async replaceById(
|
|
172
|
+
@param.path.number('id') id: number,
|
|
173
|
+
@requestBody() account: Account,
|
|
174
|
+
): Promise<void> {
|
|
175
|
+
await this.accountRepository.replaceById(id, account);
|
|
176
|
+
}
|
|
177
|
+
}
|