Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(omitable feature): now we can have omitable fields to create val… #59

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

![Node.js CI](https://github.com/herbsjs/gotu/workflows/Node.js%20CI/badge.svg?branch=master) [![codecov](https://codecov.io/gh/herbsjs/gotu/branch/master/graph/badge.svg)](https://codecov.io/gh/herbsjs/gotu)

- [Introduction](#gotu-kola)
- [Installing](#installing)
- [Using](#using)
- [Validation](#salidation)
- [Serialization](#serialization)
- [Field definition](#field-definition)
- [Scalar types](#scalar-types)
- [Method definition](#method-definition)
- [Value Objects](#value-objects)
- [Instance Type Check](#instance-type-check)
- [TODO](#TODO)
- [Contribute](#contribute)
- [The Herb](#the-herb)
- [License](#license)

# Gotu Kola

Gotu Kola helps define your business entities (*)
Expand Down Expand Up @@ -338,7 +353,40 @@ const user = new User()
const access = user.hasAccess()
```

## Instance Type Check - `Entity.parentOf`
## Value objects


The `asValueObject` method returns a new entity without `id` and `omitable` fields.

Who you could to avoid create a second object just to receive your basic entity data without processed fields?
e.g:

``` js
const User =
entity('User', {
id: id(String),
thirtyPartID: field(String, { isID: true }),
createdAt: field(Date, { omitable: true, presence: true }),
updatedat: field(Date, { omitable: true }),
deletedAt: field(Date, { omitable: true }),
name: field(String),
age: field(Number),
country: field(String)
})

// To create this user we can't receive the ID, thirtyPartID, createdAt... because this properties will be generated into runtime, to avoid this we cloud to use the asValueObject method.

const secondEntity = User.asValueObject()
/* secondEntity's fields
name: field(String),
age: field(Number),
country: field(String)
```


## Instance Type Check

### `Entity.parentOf`

Check if a instance is the same type from its parent entity class (similar to `instanceOf`)

Expand All @@ -353,7 +401,7 @@ Check if a instance is the same type from its parent entity class (similar to `i
AnEntity.parentOf(instance2) // false
```

## Entity Type Check - `entity.isEntity`
### `entity.isEntity`

Check if an object is a Gotu Entity class

Expand Down
9 changes: 8 additions & 1 deletion src/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ class EntityBuilder {
this.body = body
}

build() {
build(asValueObject = false) {
const Entity = ({[this.name] : class extends BaseEntity {}})[this.name]
Entity.prototype.meta = {
name: this.name,
schema: {}
}

for (const [name, info] of Object.entries(this.body)) {
if(asValueObject && (info.options.omitable || info.options.isId)) continue
if (!(info instanceof Field)) {
Entity.prototype[name] = info
Entity.prototype.meta.schema[name] = Function
Expand All @@ -23,6 +24,11 @@ class EntityBuilder {
info.name = name
Entity.prototype.meta.schema[name] = info
}

Entity.asValueObject = () => {
const builder = new EntityBuilder(this.name, this.body)
return builder.build(true)
}
return Entity
}
}
Expand All @@ -38,4 +44,5 @@ entity.isEntity = (instance) => {
instance.prototype instanceof BaseEntity)
}


module.exports = { entity }
2 changes: 1 addition & 1 deletion test/entity/isEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('A entity', () => {
field1: field(Number)
})
const instance1 = new AnEntity()
const instance2 = new AnEntity()
const instance2 = new (AnEntity.asValueObject())()
const instance3 = new ASecondEntity()
//then
assert.ok(entity.isEntity(AnEntity))
Expand Down
147 changes: 80 additions & 67 deletions test/entity/multipleFieldsEntity.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { entity } = require('../../src/entity')
const { field } = require('../../src/field')
const { field, Field } = require('../../src/field')
const { id } = require('../../src/customTypes/id')
const assert = require('assert')

Expand All @@ -11,32 +11,33 @@ describe('An entity', () => {
f1: field(String)
})

const givenAnEntityWithMultipleFields = () => {
const AnEntity = entity('An entity', {
field1: field(Number),
field2: field(String),
field3: field(Date),
field4: field(Boolean),
field5: field(NewEntity),
field6: field([NewEntity]),
field7: id(Number),
field8: id(Date),
field9: field(Number, { isId: true }),
field10: id(Number, { validation: { length: { is: 10 } }})
})
return new AnEntity()
const EntityWithMultipleFields = entity('An entity', {
field1: field(Number),
field2: field(String),
field3: field(Date),
field4: field(Boolean),
field5: field(NewEntity, { omitable: true, presence: true }),
field6: field([NewEntity], { omitable: true }),
field7: id(Number),
field8: id(Date),
field9: field(Number, { isId: true }),
field10: id(Number, { omitable: true, validation: { length: { is: 10 } } })
})

const anEntityInstanceWithMultipleFields = () => {
return new EntityWithMultipleFields()
}

it('should initiate', () => {
//given
const instance = givenAnEntityWithMultipleFields()
const instance = anEntityInstanceWithMultipleFields()
//then
assert.equal(instance.meta.name, 'An entity')
})

it('should set a value to multiple fields', () => {
//given
const instance = givenAnEntityWithMultipleFields()
const instance = anEntityInstanceWithMultipleFields()
//when
instance.field1 = 1
instance.field2 = "1"
Expand All @@ -59,8 +60,8 @@ describe('An entity', () => {

it('should have multiple instances with isolated valued from each other', () => {
//given
const instance1 = givenAnEntityWithMultipleFields()
const instance2 = givenAnEntityWithMultipleFields()
const instance1 = anEntityInstanceWithMultipleFields()
const instance2 = anEntityInstanceWithMultipleFields()

//when
instance1.field1 = 1
Expand All @@ -72,7 +73,7 @@ describe('An entity', () => {
instance1.field5 = newEntity
instance1.field6 = [newEntity]

const instance3 = givenAnEntityWithMultipleFields()
const instance3 = anEntityInstanceWithMultipleFields()

//then
assert.strictEqual(instance1['field1'], 1)
Expand Down Expand Up @@ -101,7 +102,7 @@ describe('An entity', () => {

it('should validate types and have valid value', () => {
//given
const instance = givenAnEntityWithMultipleFields()
const instance = anEntityInstanceWithMultipleFields()
instance.field1 = 1
instance.field2 = "1"
instance.field3 = new Date('2019-09-30T23:45:34.324Z')
Expand All @@ -117,7 +118,7 @@ describe('An entity', () => {

it('should validate types and have invalid value', () => {
//given
const instance = givenAnEntityWithMultipleFields()
const instance = anEntityInstanceWithMultipleFields()
instance.field1 = "1"
instance.field2 = 1
instance.field3 = Date('2019-09-30T23:45:34.324Z')
Expand All @@ -127,18 +128,18 @@ describe('An entity', () => {
//then
assert.strictEqual(instance.isValid(), false)
assert.deepStrictEqual(instance.errors, {
"field1": [{wrongType: 'Number'}],
"field2": [{wrongType: 'String'}],
"field3": [{wrongType: 'Date'}],
"field4": [{wrongType: 'Boolean'}],
"field5": [{wrongType: 'New Entity'}],
"field6": [{wrongType: ['New Entity']}]
"field1": [{ wrongType: 'Number' }],
"field2": [{ wrongType: 'String' }],
"field3": [{ wrongType: 'Date' }],
"field4": [{ wrongType: 'Boolean' }],
"field5": [{ wrongType: 'New Entity' }],
"field6": [{ wrongType: ['New Entity'] }]
})
})

it('should set a field as ID and have valid entity', () => {
//given
const instance = givenAnEntityWithMultipleFields()
const instance = anEntityInstanceWithMultipleFields()
instance.field7 = 1
instance.field8 = new Date('2021-12-12')

Expand All @@ -153,50 +154,62 @@ describe('An entity', () => {


it('should set a field as ID using field with isId option', () => {
//given
const instance = givenAnEntityWithMultipleFields()
instance.field9 = 1
//given
const instance = anEntityInstanceWithMultipleFields()
instance.field9 = 1

//then
assert.strictEqual(instance.__proto__.meta.schema.field9.options.isId, true)
assert.strictEqual(instance['field9'], 1)
assert.strictEqual(instance.isValid(), true)
assert.deepStrictEqual(instance.errors, {})
})
//then
assert.strictEqual(instance.__proto__.meta.schema.field9.options.isId, true)
assert.strictEqual(instance['field9'], 1)
assert.strictEqual(instance.isValid(), true)
assert.deepStrictEqual(instance.errors, {})
})

it('should validate types with invalid ID value', () => {
//given
const instance = givenAnEntityWithMultipleFields()
instance.field7 = '1'
//then
assert.strictEqual(instance.isValid(), false)
assert.strictEqual(instance['field7'], '1')
assert.deepStrictEqual(instance.errors, {
"field7": [{wrongType:'Number'}]
//given
const instance = anEntityInstanceWithMultipleFields()
instance.field7 = '1'
//then
assert.strictEqual(instance.isValid(), false)
assert.strictEqual(instance['field7'], '1')
assert.deepStrictEqual(instance.errors, {
"field7": [{ wrongType: 'Number' }]
})
})
})

it('should validate types with invalid ID value, but ignore errors using isValid({exceptIDs: true})', () => {
//given
const instance = givenAnEntityWithMultipleFields()
instance.field7 = '1'
//then
assert.strictEqual(instance.isValid({exceptIDs: true}), true)
assert.strictEqual(instance['field7'], '1')
assert.deepStrictEqual(instance.errors, {})
})

it('should validate types with valid ID value but with wrong length validation', () => {
//given
const instance = givenAnEntityWithMultipleFields()
instance.field10 = 12345678
//then
assert.strictEqual(instance.isValid(), false)
assert.strictEqual(instance['field10'], 12345678)
assert.deepStrictEqual(instance.errors, {
"field10": [{wrongLength:10}]
})
})
//given
const instance = anEntityInstanceWithMultipleFields()
instance.field7 = '1'
//then
assert.strictEqual(instance.isValid({ exceptIDs: true }), true)
assert.strictEqual(instance['field7'], '1')
assert.deepStrictEqual(instance.errors, {})
})

it('should validate types with valid ID value but with wrong length validation', () => {
//given
const instance = anEntityInstanceWithMultipleFields()
instance.field10 = 12345678
//then
assert.strictEqual(instance.isValid(), false)
assert.strictEqual(instance['field10'], 12345678)
assert.deepStrictEqual(instance.errors, {
"field10": [{ wrongLength: 10 }]
})
})

it('should returns a new entity without omitable fields', () => {
//given
const Entity = EntityWithMultipleFields.asValueObject()
const { prototype: { meta: { schema } } } = Entity
for (let i = 5; i < 11; i++)
assert.strictEqual(schema[`field${i}`], undefined)

for (let i = 1; i < 5; i++) {
assert.equal(schema[`field${i}`].constructor, Field)
}
})

})
})
4 changes: 2 additions & 2 deletions test/entity/parentOf.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ describe('A entity', () => {
field1: field(Number)
})
const instance1 = new AnEntity()
const instance2 = new AnEntity()
const instance2 = new (AnEntity.asValueObject())()
const instance3 = new AnSecondEntity()
//then
assert.ok(AnEntity.parentOf(instance1))
assert.ok(AnEntity.parentOf(instance2))
assert.ok(!AnEntity.parentOf(instance2))
assert.ok(!AnEntity.parentOf(instance3))
})
})