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
},

Dependents (0)

Package Sidebar

Install

npm i entix-cli

Weekly Downloads

1

Version

0.2.6

License

ISC

Unpacked Size

335 kB

Total Files

126

Last publish

Collaborators

  • chen7david