Necessitates Proper Modularity

    entix-cli

    0.2.6 • Public • Published

    entix-cli

    Supported Commands

    NOTE: items marked with * are optional.

    • entix app name_of_app_directory*
    • entix api name_of_entity
    • entix migration name_of_migration
    • entix client name_of_app_directory*
    • npm run dl:db
    • npm run init
    • npm run dev
    • npm run start
    • npm run seed

    Supported Flags

    • -v: displays version
    • -O: overwrites old files

    Entix App

    Default Entix Controllers

    Every Entix app comes loaded with the following base controllers. These are a collection of the most common tasks such as CRUD operations and querying relations. If you need to override one of these controllers you can do that by writing your replacement function in the controller.js file:

    • loadOne: /users/:id - loads entity in ctx.state.user.
    • find: /users/:id?p=1&l=10 - returns all instnaces with pagination support .
    • findOne: /users/:id - returns a single instance.
    • findRelation: /users/:id/roles - returns all role instances related to the user instance with :id.
    • $findRelation: /user-roles - returns all role instances related to the authenticated $user instance.
    • count: /users/count - returns the number of user records in the databse.
    • create: /users/:id - creates a new user and returns a single instance.
    • update: /users/:id - updates the user with :id returns the updated instance.
    • delete: deletes the user with :id and retuns true.
    • addRelation: /users/:id/roles - many to many links roleId to user instance with :id.
    • removeRelation: /users/:id/roles - many to many unlinks roleId from user instance with :id.
    • syncRelation: /users/:id/roles - many to many syncs the user-roles relationship to a given array of role:ids.

    NOTE: findRelation, $findRelation, addRelation, removeRelation and syncRelation rely on their route name to retrieve the relation that needs to be loaded. As such you should keep this in mind when naming your routes. Below is an example to illustrate this.

    Example 1A: In this example we will consider an user entity

    model.js
    class User extends Model {
        ...
    
        static get relationMappings(){
            return {
                emails: { ... },
                roles: { ... },
            }
        }
    }

    This user model has two relaionships. Namely, emails a has many relation and roles a many to many relation

    config.js
    module.exports = {
        routes: [
            {
                path: '/user-emails',
                method: 'get',
                handler: 'users.$findRelation',
                middleware: [
                    '@restricted.authenticated',
                ]
            },
            {
                path: '/users/:id/emails',
                method: 'post',
                handler: 'users.findRelation',
                middleware: [
                    '@load.userById',
                ]
            },
            {
                path: '/users/:id/roles',
                method: 'post',
                handler: 'roles.addRelation',
                middleware: [
                    '@load.userById',
                ]
            },
            {
                path: '/users/:id/roles',
                method: 'delete',
                handler: 'roles.removeRelation',
                middleware: [
                    '@load.userById',
                ]
            },
        ]
    }

    Notice that the handler of the /user-emails route is preceded with a $ to inducate that it will draw return relations for the authenticated user. These routes are of the format /user-relation_name. Instnace routes on the other require that you include the :id of the instance in your route and should adhere to the format /user/:id/relation_name. Consequently, this will return relations for the instance loaded with the provided :id.

    Example 2A: Models

    Entities contain 3 files, one of which is the model.js file. This file is used for writing DB schemas, relation mappings and your validation logic. In this section we will address each of these parts in detail.

    model.js
    class User extends Model {
        ...
    
        static get relationMappings(){
            return {
    
                tenant: {
                    relation: Model.BelongsToOneRelation,
                    modelClass: require('./../tenants/model'),
                    join: {
                        from: 'users.tenant_id',
                        to: 'tenants.id'
                    }
                },
    
                emails: {
                    relation: Model.HasManyRelation,
                    modelClass: require('./../emails/model'),
                    join: {
                        from: 'emails.user_id',
                        to: 'users.id',
                    },
                },
    
                roles: {
                    relation: Model.ManyToManyRelation,
                    modelClass: require('./../roles/model'),
                    join: {
                        from: 'users.id',
                        to: 'roles.id',
                        through: {
                            from: 'user_roles.user_id',
                            to: 'user_roles.role_id',
                        }
                    },
                },
            }
        }
    }
    model.js (knex schema)
    class User extends Model {
        
        static schema = (table) => {
            table.increments()
            table.string('user_id').unique().notNullable()
            table.string('username').unique().notNullable()
            table.string('password').unique().notNullable()
            table.string('avatar_path').unique()
            table.string('given_name')
            table.string('family_name')
            table.string('middle_name')
            table.enu('sex', ['male', 'female'])
            table.date('birth_date')
            table.string('birth_place')
            table.string('birth_country')
            table.boolean('is_disabled').defaultTo(false)
            table.timestamps(true,true)
        }
        ... 
    }
    model.js (knex schema:user_roles)
    static schema = (table) => {
        table.increments()
        table.unique(['user_id', 'role_id'])
        table.integer('user_id').references('id').inTable('users').onDelete('CASCADE').notNullable()
        table.integer('role_id').references('id').inTable('roles').onDelete('CASCADE').notNullable()
    }
    model.js (joi validation)
    class User extends Model {
        
        static validation = {
            
            createUser: v.object().options(joiOptions).keys({
                username: v.string().min(3).max(30).required(),
                email: v.string().email().required(),
                password: v.string().min(6).required(),
            }),
    
            updateUser: v.object().options(joiOptions).keys({
                given_name: v.string().min(1).label('given name').allow(null),
                family_name: v.string().min(1).label('family name').allow(null),
                middle_name: v.string().min(1).label('middle name').allow(null),
                sex: v.string().allow(null),
                birth_date: v.string().min(1).label('birth date').allow(null),
                birth_place: v.string().min(1).label('birth place').allow(null),
                birth_country: v.string().min(1).label('birth country').allow(null),
            }),
        }
    
        ... 
    }

    Core Services

    Uploads

    async uploadAvatar(ctx) {
    
        const { files, body } = ctx.request
        const { user } = ctx.state
    
        if(files && Object.keys(files).length > 0) {
    
            const folder = resolve(__dirname, './../../../', 'public')
            const oldPath = resolve(folder, user.avatar_path || 'placeholder.txt')
    
            if(fs.existsSync(oldPath)) await fs.promises.unlink(oldPath)
    
            const upload = await entix.services.upload.one({
                files: files,
                field: 'avatar',
                folder: folder
            })
    
            if(!upload) ctx.throw(401, 'update failed')
            body.avatar_path = upload.filename
        }
        
        const instance = await user.$query().patch(body)
        ctx.body = instance
    },

    Keywords

    none

    Install

    npm i entix-cli

    DownloadsWeekly Downloads

    6

    Version

    0.2.6

    License

    ISC

    Unpacked Size

    335 kB

    Total Files

    126

    Last publish

    Collaborators

    • chen7david