This repository has been archived by the owner on Sep 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(repository): defer to
toJSON
for hidden property exclusion (#39)
Previously, the package used a custom `excludeHiddenProps` function to exclude hidden fields. Which prevented users from accessing those fields on backend. With this change, we now defer to the LoopBack Model's built-in toJSON method, which already handles the exclusion of hidden properties when serializing data for a response. GH-37
- Loading branch information
1 parent
fa3cd07
commit 7e36924
Showing
7 changed files
with
197 additions
and
23 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,15 +9,20 @@ import { | |
StubbedInstanceWithSinonAccessor, | ||
TestSandbox, | ||
} from '@loopback/testlab'; | ||
import _ from 'lodash'; | ||
import {resolve} from 'path'; | ||
import {UniqueConstraintError} from 'sequelize'; | ||
import {SequelizeCrudRepository, SequelizeDataSource} from '../../sequelize'; | ||
import {SequelizeSandboxApplication} from '../fixtures/application'; | ||
import {config as primaryDataSourceConfig} from '../fixtures/datasources/primary.datasource'; | ||
import {config as secondaryDataSourceConfig} from '../fixtures/datasources/secondary.datasource'; | ||
import {TableInSecondaryDB} from '../fixtures/models'; | ||
import {ProgrammingLanguage, TableInSecondaryDB} from '../fixtures/models'; | ||
import {Box, Event, eventTableName} from '../fixtures/models/test.model'; | ||
import {UserRepository} from '../fixtures/repositories'; | ||
import { | ||
DeveloperRepository, | ||
ProgrammingLanguageRepository, | ||
UserRepository, | ||
} from '../fixtures/repositories'; | ||
|
||
type Entities = | ||
| 'users' | ||
|
@@ -35,6 +40,8 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
|
||
let app: SequelizeSandboxApplication; | ||
let userRepo: UserRepository; | ||
let developerRepo: DeveloperRepository; | ||
let languagesRepo: ProgrammingLanguageRepository; | ||
let client: Client; | ||
let datasource: StubbedInstanceWithSinonAccessor<SequelizeDataSource>; | ||
|
||
|
@@ -74,7 +81,83 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
it('creates an entity', async () => { | ||
const user = getDummyUser(); | ||
const createResponse = await client.post('/users').send(user); | ||
expect(createResponse.body).deepEqual({id: 1, ...user}); | ||
expect(createResponse.body).deepEqual({ | ||
id: 1, | ||
..._.omit(user, ['password']), | ||
}); | ||
}); | ||
|
||
it('returns model data without hidden props in response of create', async () => { | ||
const user = getDummyUser(); | ||
const createResponse = await client.post('/users').send(user); | ||
expect(createResponse.body).not.to.have.property('password'); | ||
}); | ||
|
||
it('[create] allows accessing hidden props before serializing', async () => { | ||
const user = getDummyUser(); | ||
const userData = await userRepo.create({ | ||
name: user.name, | ||
address: user.address as AnyObject, | ||
email: user.email, | ||
password: user.password, | ||
dob: user.dob, | ||
active: user.active, | ||
}); | ||
|
||
expect(userData).to.have.property('password'); | ||
expect(userData.password).to.be.eql(user.password); | ||
const afterResponse = userData.toJSON(); | ||
expect(afterResponse).to.not.have.property('password'); | ||
}); | ||
|
||
it('[find] allows accessing hidden props before serializing', async () => { | ||
const user = getDummyUser(); | ||
await userRepo.create({ | ||
name: user.name, | ||
address: user.address as AnyObject, | ||
email: user.email, | ||
password: user.password, | ||
dob: user.dob, | ||
active: user.active, | ||
}); | ||
|
||
const userData = await userRepo.find(); | ||
|
||
expect(userData[0]).to.have.property('password'); | ||
expect(userData[0].password).to.be.eql(user.password); | ||
const afterResponse = userData[0].toJSON(); | ||
expect(afterResponse).to.not.have.property('password'); | ||
}); | ||
|
||
it('[findById] allows accessing hidden props before serializing', async () => { | ||
const user = getDummyUser(); | ||
const createdUser = await userRepo.create({ | ||
name: user.name, | ||
address: user.address as AnyObject, | ||
email: user.email, | ||
password: user.password, | ||
dob: user.dob, | ||
active: user.active, | ||
}); | ||
|
||
const userData = await userRepo.findById(createdUser.id); | ||
|
||
expect(userData).to.have.property('password'); | ||
expect(userData.password).to.be.eql(user.password); | ||
const afterResponse = userData.toJSON(); | ||
expect(afterResponse).to.not.have.property('password'); | ||
}); | ||
|
||
it('creates an entity and finds it', async () => { | ||
const user = getDummyUser(); | ||
await client.post('/users').send(user); | ||
const userResponse = await client.get('/users').send(); | ||
expect(userResponse.body).deepEqual([ | ||
{ | ||
id: 1, | ||
..._.omit(user, ['password']), | ||
}, | ||
]); | ||
}); | ||
|
||
it('counts created entities', async () => { | ||
|
@@ -173,15 +256,23 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
delete user.active; | ||
|
||
await userRepo.execute( | ||
'INSERT INTO "user" (name, email, is_active, address, dob) VALUES ($1, $2, $3, $4, $5)', | ||
[user.name, user.email, user.is_active, user.address, user.dob], | ||
'INSERT INTO "user" (name, email, password, is_active, address, dob) VALUES ($1, $2, $3, $4, $5, $6)', | ||
[ | ||
user.name, | ||
user.email, | ||
user.password, | ||
user.is_active, | ||
user.address, | ||
user.dob, | ||
], | ||
); | ||
|
||
const users = await userRepo.execute('SELECT * from "user"'); | ||
|
||
expect(users).to.have.length(1); | ||
expect(users[0]).property('name').to.be.eql(user.name); | ||
expect(users[0]).property('email').to.be.eql(user.email); | ||
expect(users[0]).property('password').to.be.eql(user.password); | ||
expect(users[0]).property('address').to.be.eql(user.address); | ||
expect(new Date(users[0].dob)).to.be.eql(new Date(user.dob!)); | ||
expect(users[0]).property('is_active').to.be.ok(); | ||
|
@@ -200,7 +291,7 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
delete user.active; | ||
|
||
await userRepo.execute( | ||
'INSERT INTO "user" (name, email, is_active, address, dob) VALUES ($name, $email, $is_active, $address, $dob)', | ||
'INSERT INTO "user" (name, email, password, is_active, address, dob) VALUES ($name, $email, $password, $is_active, $address, $dob)', | ||
user, | ||
); | ||
|
||
|
@@ -209,6 +300,7 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
expect(users).to.have.length(1); | ||
expect(users[0]).property('name').to.be.eql(user.name); | ||
expect(users[0]).property('email').to.be.eql(user.email); | ||
expect(users[0]).property('password').to.be.eql(user.password); | ||
expect(users[0]).property('address').to.be.eql(user.address); | ||
expect(new Date(users[0].dob)).to.be.eql(new Date(user.dob!)); | ||
expect(users[0]).property('is_active').to.be.ok(); | ||
|
@@ -411,16 +503,17 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
await migrateSchema(['developers']); | ||
|
||
const programmingLanguages = [ | ||
getDummyProgrammingLanguage({name: 'JS'}), | ||
getDummyProgrammingLanguage({name: 'Java'}), | ||
getDummyProgrammingLanguage({name: 'Dot Net'}), | ||
getDummyProgrammingLanguage({name: 'JS', secret: 'Practice'}), | ||
getDummyProgrammingLanguage({name: 'Java', secret: 'Practice'}), | ||
getDummyProgrammingLanguage({name: 'Dot Net', secret: 'Practice'}), | ||
]; | ||
const createAllResponse = await client | ||
.post('/programming-languages-bulk') | ||
.send(programmingLanguages); | ||
|
||
const createDeveloperResponse = await client.post('/developers').send( | ||
getDummyDeveloper({ | ||
apiSecret: 'xyz-123-abcd', | ||
programmingLanguageIds: createAllResponse.body.map( | ||
(language: {id: number}) => language.id, | ||
), | ||
|
@@ -451,6 +544,51 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
}); | ||
}); | ||
|
||
it('hides hidden props for nested entities included with referencesMany relation', async () => { | ||
await developerRepo.syncLoadedSequelizeModels({force: true}); | ||
|
||
const programmingLanguages = [ | ||
getDummyProgrammingLanguage({name: 'JS', secret: 'woo'}), | ||
getDummyProgrammingLanguage({name: 'Java', secret: 'woo'}), | ||
getDummyProgrammingLanguage({name: 'Dot Net', secret: 'woo'}), | ||
]; | ||
|
||
const createAllResponse = await languagesRepo.createAll( | ||
programmingLanguages, | ||
); | ||
expect(createAllResponse[0]).to.have.property('secret'); | ||
|
||
const createDeveloperResponse = await developerRepo.create( | ||
getDummyDeveloper({ | ||
apiSecret: 'xyz-123-abcd', | ||
programmingLanguageIds: createAllResponse.map( | ||
(language: ProgrammingLanguage) => language.id, | ||
), | ||
}), | ||
); | ||
|
||
const filter = {include: ['programmingLanguages']}; | ||
const relationRes = await developerRepo.findById( | ||
createDeveloperResponse.id, | ||
filter, | ||
); | ||
|
||
if (primaryDataSourceConfig.connector === 'sqlite3') { | ||
/** | ||
* sqlite3 doesn't support array data type using it will convert values | ||
* to comma saperated string | ||
*/ | ||
createDeveloperResponse.programmingLanguageIds = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
createDeveloperResponse.programmingLanguageIds.join(',') as any; | ||
} | ||
|
||
expect(relationRes.toJSON()).to.be.deepEqual({ | ||
...createDeveloperResponse.toJSON(), | ||
programmingLanguages: createAllResponse.map(e => e.toJSON()), | ||
}); | ||
}); | ||
|
||
it('supports INNER JOIN', async () => { | ||
await migrateSchema(['books']); | ||
|
||
|
@@ -715,6 +853,8 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
await app.start(); | ||
|
||
userRepo = await app.getRepository(UserRepository); | ||
developerRepo = await app.getRepository(DeveloperRepository); | ||
languagesRepo = await app.getRepository(ProgrammingLanguageRepository); | ||
datasource = createStubInstance(SequelizeDataSource); | ||
client = createRestAppClient(app as RestApplication); | ||
} | ||
|
@@ -728,6 +868,7 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
email: string; | ||
active?: boolean; | ||
address: AnyObject | string; | ||
password?: string; | ||
dob: Date | string; | ||
} & AnyObject; | ||
|
||
|
@@ -736,6 +877,7 @@ describe('Sequelize CRUD Repository (integration)', function () { | |
email: '[email protected]', | ||
active: true, | ||
address: {city: 'Indore', zipCode: 452001}, | ||
password: 'secret', | ||
dob: timestamp, | ||
...overwrite, | ||
}; | ||
|
Oops, something went wrong.