Coder Social home page Coder Social logo

herbsjs / gotu Goto Github PK

View Code? Open in Web Editor NEW
11.0 6.0 19.0 1.58 MB

Entities - Create your Entity and enjoy an elegant validation and serialization

License: Other

JavaScript 100.00%
entity javascript clean-architecture domain-driven-design ddd schema hacktoberfest

gotu's Introduction

Node.js CI codecov

Gotu Kola

Gotu Kola helps define your business entities (*)

(*) Entities: they are the first natural place we should aim to place business logic in domain-driven applications.

Installing

$ npm install @herbsjs/gotu

Using

const { entity, field } = require('@herbsjs/gotu')

const Feature =
        entity('Feature', {
            name: field(String),
            hasAccess: field(Boolean)
        })

const Plan =
    entity('Plan', {
        name: field(String),
        monthlyCost: field(Number)
    })

const User =
    entity('User', {
        name: field(String),
        lastAccess: field(Date),
        accessCount: field(Number),
        features: field([Feature]),
        plan: field(Plan),
    })

const user = new User()
user.name = "Beth"
user.plan.monthlyCost = 10
user.features = [
    new Feature(),
    new Feature(),
    new Feature()
]
user.validate()

Deep copy entity

To clone the entity, we can use entity.fromJSON(), like the example below:

const Customer =
    entity('Customer', {
        name: field(String),
        age: field(Number),
        gender: field(String)
    })
const customer1 = new Customer()
customer1.name = 'John Doe'
customer1.age = 20
customer1.gender = 'Male'
const customer2 = Customer.fromJSON(customer1)
//customer1.name = 'John Doe'
//customer1.age = 20
//customer1.gender = 'Male'

To do a clone changing original entity, you can use desestructuring method, like this:

const Customer =
    entity('Customer', {
        name: field(String),
        age: field(Number),
        gender: field(String)
    })
const customer1 = new Customer()
customer1.name = 'John Doe'
customer1.age = 20
customer1.gender = 'Male'
const customer2 = Customer.fromJSON({ ...customer1, name: 'Billy Jean' })
//customer1.name = 'Billy Jean'
//customer1.age = 20
//customer1.gender = 'Male'

Validation

A value of an field can be validated against the field type or its custom validation.

Type Validation

const Plan =
    entity('Plan', {
        ...
        monthlyCost: field(Number),
    })

const User =
    entity('User', {
        name: field(String),
        plan: field(Plan)
    })

const user = new User()
user.name = 42
user.plan.monthlyCost = true
user.validate()
user.errors // { name: [ wrongType: 'String' ], plan: { monthlyCost: [ wrongType: 'Number' ] } }
user.isValid() // false

You can also simplify you validation method using isValid() method that execute validate implicit for you entity and return true/false in a single execution and also you can check the errors.

const Plan =
    entity('Plan', {
        ...
        monthlyCost: field(Number),
    })

const plan = new Plan()
plan.plan.monthlyCost = true
plan.isValid() // false
plan.errors // { monthlyCost: [ wrongType: 'Number' ] }

You can ignore id field validation using isValid({exceptIDs: true}). Example: Imagine that your id should not be null, but sometimes, in an insertion case, the ID only exists after an insertion in the database, so you can validate the hole entity, except the id field.

const Plan =
    entity('Plan', {
        ...
        myId: id(Number),
        monthlyCost: field(Number),
    })

const plan = new Plan()
plan.plan.myId = '123'
plan.plan.monthlyCost = 500
plan.isValid({exceptIDs: true}) // true

plan.isValid() // false
plan.errors // { }

You can validate only id field validation using isValid({onlyIDs: true}). Example: Imagine that your entity you want to validate only the id because you should insert first the id.

const Plan =
    entity('Plan', {
        ...
        myId: id(Number),
        monthlyCost: field(Number),
    })

const plan = new Plan()
plan.plan.myId = 123
plan.plan.monthlyCost = '500'
plan.isValid({onlyIDs: true}) // true

plan.isValid() // false
plan.errors // { myId: [ wrongType: 'Number' ] }

You can validate references too. Gotu will automatically validate subentities, however with the reference key, you can select whether you want to validate only the Ids or except the Ids

            const AnEntity1 = entity('A entity', {
                id1: id(Number, { validation: { presence: true } }),
                field1: field(String, { validation: { presence: true } })
            })
            const AnEntity2 = entity('A entity', {
                id21: id(Number, { validation: { presence: true } }),
                id22: id(String, { validation: { presence: true } }),
                field2: field(String, { validation: { presence: true } }),
                fieldEntity2: field(AnEntity1, { validation: { presence: true } }),
                fieldEntities2: field([AnEntity1], { validation: { presence: true } })
            })
            const AnEntity3 = entity('A entity', {
                id3: id(Number, { validation: { presence: true } }),
                field3: field(String, { validation: { presence: true } }),
                fieldEntity3: field(AnEntity2, { validation: { presence: true } })
            })
            const instance = AnEntity3.fromJSON({
                id3: '3',
                field3: undefined,
                fieldEntity3: {
                    id21: '2',
                    id22: 2,
                    field2: 'value2',
                    fieldEntity2: { id1: undefined, field1: 'value1' },
                    fieldEntities2: [
                        { id1: '1', field1: undefined },
                        { id1: undefined, field1: 'value1' }]
                }
            })


            instance.validate({ references: { onlyIDs: true } })

            instance.errors /* 
               { id3: [{ wrongType: 'Number' }],
                 field3: [{ cantBeEmpty: true }],
                    fieldEntity3: {
                       id21: [{ wrongType: 'Number' }],
                      id22: [{ wrongType: 'String' }],
                  }})}*/

Custom Validation

For custom validation Gotu uses Herbs JS Suma library under the hood. It has no message defined, only error codes.

Use { validation: ... } to specify a valid Suma validation (sorry) on the field definition.

const User =
    entity('User', {
        ...
        password: field(String, { validation: {
            presence: true,
            length: { minimum: 6 } }
        }),
        cardNumber: field(String, { validation: {
          custom: { invalidCardNumber: (value) => value.length === 16 } }
        })
    })

const user = new User()
user.password = '1234'
user.cardNumber = '1234456'
user.validate()
user.errors // [{ password: [ { isTooShort: 6 } ] , { "invalidCardNumber": true }]
user.isValid // false

Serialization

fromJSON(value)

Returns a new instance of a entity

const User =
    entity('User', {
        name: field(String)
    })

// from object
const user = User.fromJSON({ name: 'Beth'})
// or string
const user = User.fromJSON(`{ "name": "Beth"}`)

By default fromJSON serializes only keys that have been defined in the entity. If you want to add other keys during serialization, use .fromJSON(data, { allowExtraKeys: true }).

By default, fromJSON default field values will be applied for keys not present in value.

JSON.stringify(entity)

To serialize an entity to JSON string use JSON.stringify or entity.toJSON function.

const json = JSON.stringify(user) // { "name": "Beth" }

By default toJSON serializes only keys that have been defined in the entity. If you want to add other keys during serialization, use entity.toJSON({ allowExtraKeys: true }).

Field definition

A entity field type has a name, type, default value, validation and more.

Scalar types

A field in an entity can be of basic types, the same as those used by JavaScript:

Number: double-precision 64-bit binary format IEEE 754 value

String: a UTF‐16 character sequence

Boolean: true or false

Date: represents a single moment in time in a platform-independent format.

const User =
    entity('User', {
        name: field(String),
        lastAccess: field(Date),
        accessCount: field(Number),
        hasAccess: field(Boolean)
    })

Entity type

For complex types, with deep relationship between entities, a field can be of entity type:

const Plan =
    entity('Plan', {
        ...
        monthlyCost: field(Number),
    })

const User =
    entity('User', {
        ...
        plan: field(Plan)
    })

List of Entity type

For complex types, with deep relationship between entities, a field can contain a list of entity type:

const Plan =
    entity('Plan', {
        ...
        monthlyCost: field(Number),
    })

const User =
    entity('User', {
        ...
        plan: field([Plan])
    })

ID Fields

It is possible to declare a field as an ID. This metadata will be used by glues to enrich the infrastructure interfaces (Database, REST, GraphQL, etc).

We can do it in two ways:

// The explicit way
const User =
    entity('User', {
        id: field(Number, { isId: true }),
        ...
    })

// The short way
const User =
    entity('User', {
        id: id(Number),
        ...
    })

Default value

It is possible to define a default value when an entity instance is initiate.

const User =
    entity('User', {
        ...
        hasAccess: field(Boolean, { default: false })
    })


const user = new User()
user.hasAccess // false

If the default value is a function it will call the function and return the value as default value:

const User =
    entity('User', {
        ...
        hasAccess: field(Boolean, { default: () => false })
    })


const user = new User()
user.hasAccess // false

For scalar types a default value is assumed if a default value is not given:

Type Default Value
Number 0
String ""
Boolean false
Date null

For entity types the default value is a new instance of that type. It is possible to use null as default:

const User =
    entity('User', {
        ...
        plan: field(Plan, { default: null })
    })

const user = new User()
user.plan // null

Method definition

A method can be defined to create custom behaviour in an entity:

const User =
    entity('User', {
        ...
        role: field(String),
        hasAccess() { return this.role === "admin" },
    })

const user = new User()
const access = user.hasAccess()

Instance Type Check - Entity.parentOf

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

        const AnEntity = entity('A entity', {})
        const AnSecondEntity = entity('A second entity', {})

        const instance1 = new AnEntity()
        const instance2 = new AnSecondEntity()

        AnEntity.parentOf(instance1) // true
        AnEntity.parentOf(instance2) // false

Entity Type Check - entity.isEntity

Check if an object is a Gotu Entity class

        const AnEntity = entity('A entity', {})

        const instance1 = new AnEntity()

        entity.isEntity(AnEntity) // true
        entity.isEntity(Object) // false

Entity schema - entity.schema

It is possible to access the schema information of your entity. There are two special properties:

fields

Returns an list with all the fields of the entity:

const User = entity('User', {
    id: id(Number),
    name: field(String, { validation: { presence: true }})
})

const fields = User.schema.fields

// results in
// [
//   Field {
//     name: 'id',
//     type: [Function: Number],
//     options: { isId: true },
//     _validations: null
//   },
//   Field {
//     name: 'name',
//     type: [Function: String],
//     options: { validation: [Object] },
//     _validations: null
//   }
// ]

If the entity has no field, so it returns an emtpy array ([])

ids

Returns an list with all the ids of the entity:

const User = entity('User', {
    id: id(Number),
    anotherId: field(String, { isId: true }),
    name: field(String)
})

const ids = User.schema.ids

// results in
// [
//   Field {
//     name: 'id',
//     type: [Function: Number],
//     options: { isId: true },
//     _validations: null
//   },
//   Field {
//     name: 'anotherId',
//     type: [Function: String],
//     options: { isId: true },
//     _validations: null
//   }
// ]

If the entity has no id, so it returns an emtpy array ([])

Another way to verify if a specific property is an id, is accessing the isId property in the field's options, like this:

const user = new User()

//should be equals ```true```
user.__proto__.meta.schema.id.options.isId

TODO

  • Field basic JS type definition and validation (ex: "name": String)
  • Field entity type definition and validation (ex: "user": User)
  • Field enum type definition and validation (ex: "paymentType": ['CC', 'Check'])
  • Field list type definition and validation (ex: "users": [User])
  • Entity custom methods (ex: payment.calculate())
  • Default values
  • Entity (complex) validation (ex: payment.validate() )
  • Field validation error message (ex: payment.errors )
  • Field validation error code (ex: payment.errors )
  • Entity hidrate (ex: fromJson)
  • Entity serialize (ex: toJson)
  • Extend / Custom field validation (ex: email, greater than, etc)
  • Valitation contexts (ex: Payment validation for [1] credit card or [2] check)
  • Conditional Validation (ex: if email is present, emailConfirmation must be present)
  • Entities Inheritance (schema and validations inheritance)

Contribute

Come with us to make an awesome Gotu.

Now, if you do not have technical knowledge and also have intend to help us, do not feel shy, click here to open an issue and collaborate their ideas, the contribution may be a criticism or a compliment (why not?)

If you would like to help contribute to this repository, please see CONTRIBUTING

The Herb

Gotu Kola has been used historically to relieve congestion from upper respiratory infections and colds and for wound healing. It is very popular for treating varicose veins and memory loss.

https://www.herbslist.net/

https://en.wikipedia.org/wiki/Centella_asiatica

License

Gotu is released under the MIT license.

gotu's People

Contributors

anderson-costa-vx avatar cpp-vortx avatar dalssoft avatar dependabot[bot] avatar diogooliveira-tech avatar dlojudice-vortx avatar endersoncosta avatar euduardo avatar italojs avatar jhomarolo avatar jhomarolo-vortx avatar m7vicente avatar m7vicente-vortx avatar maikmb avatar maikvortx avatar marcelobd avatar semantic-release-bot avatar victortbx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

gotu's Issues

ID field

In order to improve metadata about which fields represent the IDs of an entity it would be possible to declare as field like:

const Customer = 
    entity('Customer', {
        id: id(Number),    <---- id definition
        name: field(String),
        isVIP() {
            ...
        }
    })

for composite IDs (ex: id + SSN):

const Customer = 
    entity('Customer', {
        id: id(Number),    <---- id definition
        SSN: id(Number),    <---- id definition
        name: field(String),
        isVIP() {
            ...
        }
    })

metadata:

Customer.prototype.meta.schema.id.isID //true
Customer.prototype.meta.schema.SSN.isID //true

This would help in:

  • CLI: better use case generation
  • 2Knex: better repository generation (no ID info required)
  • 2REST and 2GQL: better resolver code

fromJSON() should initialize field values accordingly to the entity.

When created an entity instance using fromJSON() method, in some cases, the entire json object has all attributes defined as string, but these values are not ignored by fromJSON(), they just set these values in the created instance.

Since parse function in fromJSON can create BaseEntity and Dates from string types it's possible to parse Numbers and strings accordingly to the definition in the entity.

field validation based on another field condition

It should be possible to validate sinbling fields based on a condition.

Something like this:

const User = 
    entity('Person', {
        name: field(String),
        hasChildren: field(Boolean),
        childrenNames: field([String]).when('hasChildren', {
            is: true, // possibility to use a function instead
            then: ({ validation: { presence: true } }),
            // otherwise: some other validation
        }),
    })

inspired on yup .when()

what do you guys think?

Bug at toJSON method when trying to return an entity that has an array of strings

Describe the bug
When trying to return an entity that has an array of strings the gotu method "toJSON" treats each value of the array as an object and turn them into objects.

To Reproduce
Steps to reproduce the behavior:

  1. Create an entity that has an array of strings
  2. Instance the entity
  3. use the toJSON method from the instance
  4. See error

Expected behavior
Expected the array of strings to continue being an array of strings.

Screenshots
Entity
image

Entity toJSON response
image

Validation if the entity is not working properly

Describe the bug
The isEntity function is not properly reflecting whether or not it is an entity, as the validation whether it is an instance of BaseEntity is returning false.

entity.isEntity = (instance) => {
    return (
        instance instanceof BaseEntity || 
        instance.prototype instanceof BaseEntity)
}

To Reproduce
Steps to reproduce the behavior:

The issue can be reproduced by invoking entity.isEntity on an instance of BaseEntity within the buildAllFields method of the DataMapper class in Herbs2Mongo.
This has been tested using nested entities.

Expected behavior
The isEntity function should return true for all instances of BaseEntity.

Proposed solution
When calling instance.toString() we receive a string class extends BaseEntity {}, so it is possible to validate that the instance converted to string includes extends BaseEntity

entity.isEntity = (instance) => {
    return (
        instance instanceof BaseEntity ||
        instance.prototype instanceof BaseEntity ||
        instance.toString()?.includes('extends BaseEntity'))
}

Screenshots
Here's a print of the problem happening when Herbs2Mongo tries to do this validation:
Herbs2Mongo

Additional context
Resolution of issue herbsjs/herbs2mongo#35 depends on this bug being fixed.

discord test

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Convert Gotu entity to GraphQL objetct Type

Hi, I think this can help you convert Gotu entities to GraphQL types. Any suggestion?

const User = 
    entity('User', {
        name: field(String, { presence: true }),
        age: field(String, { presence: false })
    })

// from hash
const user = User.fromJSON({ name: 'Beth'})

// to graphQL
user.toGraphQL();

// print
gql`
  type User{
    name: String!
    age: Int
  }
`;

Computed field for entity

Describe the solution you'd like
Like in Object-oriented design, I'd like to read a field from an entity based on values of other fields. This would bring a concept of encapsulation to herbs' entities. This property, ideally, should not be able to be modified.

For example:

var person = new Person();
person.Name = "Victor;
person.Surname = "Melias";
console.log(person.Fullname); //should return "Victor Melias"

Additional context
In OOD languages this can be done by setting the properties with "private set", but there's no alternative in Herbs to do this without any kind of workaround

Shorthand to access ids e fields metadata

Is your feature request related to a problem? Please describe.
It would be nice to have an API to access the Ids and Fields metadata from an entity.

You can already access those metadata, but the API is not user friendly.

For example, to check if an property is an id, it is the necessary code:

const User = entity('User', {
    id: id(Number),
   name: field(String)
})

const user = new User()

const isId = user.__proto__.meta.schema.id.options.isId

Describe the solution you'd like
I would like to access those fields from a static field or method in the entity class. Something like:

const User = entity('User', {
    id: id(Number),
   name: field(String)
})

const userIds = User.schema.ids
const userFields = User.schema.fields

Additional context
There are some open issues that could use this feature without reimplementing it again.

Default value ignored when use fromJSON

Describe the bug

Given my entity:

const { entity, field } = require('gotu')
const { v4 } = require("uuid")

const Especie = entity('Especie', {
  id: field(String, {default: () => v4()}),
  nome: field(String),
  codigo: field(String),
  siglaPais: field(String)
})

When I use var especie = Especie.fromJSON(ctx.req), the id field comes empty if I do not pass the value of id in the parameter of fromJSON function.

I believe that is a bug, since gotu returns a new instance from this object within the values were send to the function.

Expected behavior
When I use fromJSON all default variables that are not passed through the function, are filled with the respective default values

Additional context
This, could be a flag optional or not inside the fromJSON function or inside de default value, maybe.

fromJSON should be from fromObject

The serialization method 'fromJSON' is capable to parse in entity not only json strings, but entire objects, so, it should be called 'fromObject'.

isValid() won't validate if number is not Defined

When we create an entity from new, number fields start undefined but, when we try to validate, they are skipped isValid() method, to get validation results is necessary to manually declare them as null.

Gotu number validator

custom validation function

It should be possible to create custom validations functions and validate the entity by calling validate() only

this:

const User = 
    entity('User', {
        name: field(String),
        lastAccess: field(Date),
        accessCount: field(Number),
        hasAccess: field(Boolean),
        plan: field(Plan),
        validations:[
        	function validateBla () {
        	},
        	function validateFoo () {
        	}
        ]
    })

or something like this

const User = 
    entity('User', {
        name: field(String, validation: () => { this.name === "John" } ),
        lastAccess: field(Date),
        accessCount: field(Number),
        hasAccess: field(Boolean),
        plan: field(Plan),
    })

Default value ignored when use fromJSON

Describe the bug

Given my entity:

const { entity, field } = require('gotu')
const { v4 } = require("uuid")

const Especie = entity('Especie', {
  id: field(String, {default: () => v4()}),
  nome: field(String),
  codigo: field(String),
  siglaPais: field(String)
})

When I use var especie = Especie.fromJSON(ctx.req) , the id field comes empty if I do not pass the value of id in the parameter of fromJSON function.

I believe that is a bug, since gotu returns a new instance from this object within the values were send to the function.

Expected behavior
When I use fromJSON all default variables that are not passed through the function, are filled with the respective default values

Additional context
This, could be a flag optional or not inside the fromJSON function or inside de default value, maybe.

Partial or Custom Entity Validation

Problem

When defining a entity, the validation rules are defined for all the fields, but sometimes we want to have different validation rules for different scenarios.

Partial or Custom Validation

for this use case we can think in several approaches:

(1) "Manual" Check for error return

Without extra implementation, it would be possible to check the returned error code:

if(!user.isValid()){ 
	if (user.error...) {
		// check if the only error came from `createdAt`, regarding `presence`.
		// we could create a helper function to do that
	}
}

This can be a bit verbose, but feasible.

(2) Validation Ignore or Override - Specific

This would make the caller to be able to ignore or override the validation rules for a specific field.

This is important because the validation are defined in the context of the caller and might be very specific to be generalized.

if(!user.isValid({ ignore: { createdAt: "presence"} })){ 

or

if(!user.isValid({ override: { createdAt: { presence: false } } })){ 

(3) Validation Contexts - Generalization

Let´s say there is a context validation that can be reused in several scenarios. For instance, the dev started with a specific validation for a specific scenario, but then realized that the same validation can be used in other scenarios.

There could have be a extra metadata on the entity for validation scenarios, like:

const User = entity('User', {
	id: id(Number, { presence: true }),
	name: field(String, { presence: true }),
	email: field(String, { presence: true }),
	createdAt: field(Date, { presence: true }),

    // this 
	create: validation({
            override: { createdAt: { presence: false } },
            ignore: { email: "presence" } 
	}),

    // or
    validations: {
        create: {
            override: { createdAt: { presence: false } },
            ignore: { email: "presence" } 
        }
    }    
})

to use it:

if(!user.isValid({ context: 'create' })){ 

Of course, the suggested code and names are just that, suggestions. Ideas on how to improve this are welcome.

(4) Conditional Validation

This is a more complex approach, but it would allow to define a validation rule that would be executed only if a condition is met.

const User = entity('User', {
    id: id(Number, { presence: true }),
    name: field(String, { presence: true }),
    email: field(String, { presence: true }),
    createdAt: field(Date, { presence: true })

    validations: {
        create: {
            condition: (user) => user.createdAt === null, // or a function that returns a boolean
            override: { createdAt: { presence: false } },
        }
    }    
})

To use it:

if(!user.isValid()) { // no need to pass the context here - if the condition is met, the validation will be executed 

I feel this could be polished a bit more. Ideas are welcome.

Error return

The error returned should be the same, regardless of the approach (1), (2), (3) or (4)

user.errors // {"password":[{"cantBeNull":true}]}

Conclusion

I would suggest start with (2), since it can help in many scenarios, and then move to (3) and (4) if needed.

Benchmarks

    class User < ApplicationRecord
        with_options if: :is_admin? do |admin|
            admin.validates :password, length: { minimum: 10 }
            admin.validates :email, presence: true
        end
    end

Reference

This issue is a spin off from #49

entity.isEntity is not working is some cases

Describe the bug
After updating the project with a new version inside herbs (which had nothing to do with the gotu), the isEntity method simply stopped returning true for fields that are entities of other entities.

And so the insert method also stopped working, because herbs2knex does a filter of fields that are entities before inserting.

But after doing that check here: new String(instance.__proto__).valueOf() === new String(BaseEntity).valueOf()
the return is true.

I'm having a hard time figuring out why.

image

To Reproduce
To reproduce, just download the whole project and update herbs to the latest version 1.6.1

Expected behavior

 entity('To Do List', {
        id: id(Number),
        name: field(String, {
            validation: { presence: true, length: { minimum: 3 } }
        }),
        items: field([Item], { default: () => [] }),    /*<--- HERE the method is entity is returning false!*/

       })

Additional context
I don't know if it's a problem with herbs2knex, but it hasn't been updated either.

Gotu Entity Clone

Is your feature request related to a problem? Please describe.
Gotu must have a method clone in order to be able to create a copy of an instance.

Describe the solution you'd like
This methods can accepts an optional overwrite object that permits you to overwrite some attributes of the copy.

Additional context

const User = 
    entity('User', {
        name: field(String),
        lastAccess: field(Date),
        accessCount: field(Number),
        features: field([Feature]),
        plan: field(Plan),
    })

const user = new User()
user.name = "Beth"
user.plan.monthlyCost = 10
user.features = [ 
    new Feature(),
    new Feature(),
    new Feature()
]

const cloneUserWithNoOverwrite = user.clone(); // User { name: 'Me }
const cloneWithOverwrite = user.clone({ name: 'Myself' }); // User { name: 'Myself' }

Create a new instance of a entity using constructor

Hello everyone!

Gotu has the FromJson method to create a new instance of an entity, but the constructor is more efficient to initialize a new instance from any input in json like this:

const user = new User({ ...input });

Can this be classified as an improvement?

omit entity properties into usecase

I would like to have a simlilar feature like ZOD omit feature

The idea is to avoid create multi entities when I need a valueobject for my usecase, e.g

const User = entitity('User', {
id: field(string),
name: field(string)
}

usecase(
    'create user',
    {
      request: User, // I dont want to receive id here
    [...] 

My propose is:

const User = entitity('User', {
id: field(string),
name: field(string)
}

// option #1
usecase(
    'create user',
    {
      request: User.toValueObject() // omit default values: id, createdAt, deleteAt, updatedAt
    [...] 
// option #2
// we can merge it with option 1
usecase(
    'create user',
    {
      request: User.omit([ 'id', 'createdAt', 'myOtherProperty']) 
    [...] 
// option #3
const User = entitity('User', {
id: field(string, { omitable: true } ),
myCustomProp: field(Number, { omitable: true }
name: field(string)
}

usecase(
    'create user',
    {
      request: User.toValueObject() // Omit 'id' and  'myCustomProp'
    [...] 

Do not add key errors to the entity when there is no validation error.

Is your feature request related to a problem? Please describe.
When I call the isValid function on my entity instance, it returns true or false, indicating whether or not my entity is valid.
Additionally, this function includes in my entity an errors key containing the errors that were detected in the validation, according to the rules defined in the entity.
However, even when there is no error, it still creates the errors key in the entity, containing an empty object.

This can lead to problems, for example if I pass the already validated entity to another usecase, or if I pass it to some function to insert into the database, which will insert the errors key containing an empty object.

Describe the solution you'd like
I believe we can make a conditional to only insert the errors key if there really is a validation error.
In the baseEntity.js file in the line that follows:

this.errors = errors

Describe alternatives you've considered
I have not considered alternatives to this feature.

Additional context
I noticed on two points:

  1. By adapting my project that uses herbs2mongo to save nested entities in the database.
  2. When using a usecase that passes the request it received (entity) to another usecase.

As a temporary fix, after verifying that the entity is valid, I deleted the errors field before calling the other usecase.

delete client.errors;

And in the database part, I changed my DataMapper instance to delete keys that I might configure when calling my deletekeys function, as already mentioned in this issue: herbsjs/herbs2mongo#35

We need to accept a list of entity

We need to accept in the field a array that should validate a type of entity.

Something like:

field( [ User ] )

This is an useful feature, which proved to be quite necessary to increase the exactitude of our entities, and then improve our tests and make the maintenance easier.

Herbs Container DI for applications

Describe the solution you'd like

The purpose of this tool is to make it more scalable and make service dependency management easier in your applications that uses gotu usecases and services and take advantage of the inversion of control pattern to loosely couple the components of your application and make them easier to test and maintain

This library can resolve services using Proxy to lazy loading services requesting and register multiples services types like:

  • Function
  • Class
  • Single Value

Describe alternatives you've considered

Additional context

This library can be found in herbs-di and a example of express middleware

container
    .addAsFunction(userRepository)
    .addAsClass(UserService)
    .addAsValue('cache', cache)
    .addAsFunction(getUsersUseCase);

const useCase = container.factoryContainer().getUsersUseCase;
var output = await useCase.run({ name: 'Kenedy' });

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.