Git Product home page Git Product logo

ditojs / dito Goto Github PK

View Code? Open in Web Editor NEW
81.0 81.0 7.0 1.09 GB

Dito.js is a declarative and modern web framework with a focus on API driven development, based on Objection.js, Koa.js and Vue.js – Developed at Lineto by Jürg Lehni and made available by Lineto in 2018 under the MIT license

Home Page: https://lineto.com

License: MIT License

JavaScript 68.05% Vue 29.24% Shell 0.01% SCSS 2.71%
api declarative json koa objection rest server sql vue

dito's People

Contributors

dependabot[bot] avatar fpatrik avatar koskimas avatar lehni avatar ollisal avatar ptoussai avatar puckey avatar rivertam avatar wirsing avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dito's Issues

Typescript types

I was looking at how Typescript types might be approached in the library using the Application configuration object as a starting point. One nicety is that you can easily import the types from underlying libraries. See for example the app.cors configuration..

Some other things I noticed while doing the initial typing:

  • env could default to process.env.NODE_ENV when not supplied
  • server.host could default to env.NODE_HOST || env.HOST || '0.0.0.0' when not supplied
  • server.port could default to env.NODE_PORT || env.PORT || 8080 when not supplied
  • log and server could be made optional like app is
import type { Config } from 'knex';
import type { KnexSnakeCaseMappersFactory } from 'objection';
import type { Options as CorsOptions } from '@koa/cors';
import type { CompressOptions } from 'koa-compress';
import type koaSession from 'koa-session';

export interface Configuration {
  /**
   * @defaultValue `production`
   */
  env?: 'production' | 'development';
  /**
   * The server configuration
   */
  server: {
    /**
     * The ip address or hostname used to serve requests
     */
    host: string;
    /**
     * The port to listen on for connections
     */
    port: number;
  };
  /**
   * Logging options
   */
  log: {
    /**
     * Enable logging requests to console by passing `true` or pick between
     * 'console' for logging to console and 'file' for logging to file
     * @defaultValue `false`
     */
    requests?: boolean | 'console' | 'file';
    /**
     * Whether to output route (Controller) logs
     * @defaultValue `false`
     */
    routes?: boolean;
    /**
     * Whether to log relation mappings
     * @defaultValue `false`
     */
    relations?: boolean;
    /**
     * Whether to log json schema
     * @defaultValue `false`
     */
    schema?: boolean;
    /**
     * Whether to log sql queries
     * @defaultValue `false`
     */
    sql?: boolean;
  };
  app?: {
    /**
     * Whether to normalize paths from camel case to kebab case
     * @defaultValue `false`
     */
    normalizePaths?: boolean;
    /**
     * @defaultValue `false`
     */
    proxy?: boolean;
    /**
     * Whether to include X-Response-Time header in responses
     * @defaultValue `true`
     */
    responseTime?: boolean;
    /**
     * Whether to use koa-helmet middleware which provides important security
     * headers to make your app more secure by default.
     * @defaultValue `true`
     */
    helmet?: boolean;
    /**
     * Enable or configure Cross-Origin Resource Sharing (CORS)
     * @defaultValue `true`
     */
    cors?: boolean | CorsOptions;
    /**
     * Enable or configure server response compression
     * @defaultValue `true`
     */
    compress?: boolean | CompressOptions;
    /**
     * Enable ETag headers in server responses
     * @defaultValue `true`
     */
    etag?: boolean;
    /**
     * @defaultValue `false`
     */
    session?: boolean | ({ modelClass: 'string' } & koaSession.opts);
    /**
     * Enable passport authentication middleware
     * @defaultValue `false`
     */
    passport?: boolean;

    // csrf: boolean,            // TODO: Implement

    /**
     * Keys used for session
     */
    keys?: string[];
  };
  knex: {
    /**
     * The database client to use - see http://knexjs.org/#Installation-client
     */
    client: Config['client'];
    /**
     * The database connection configuration - see http://knexjs.org/#Installation-client
     */
    connection: Config['connection'];
    /**
     * @defaultValue `false`
     */
    normalizeDbNames?: boolean | Parameters<KnexSnakeCaseMappersFactory>;
    /**
     * Whether to replace undefined keys with NULL instead of DEFAULT
     * @defaultValue `false`
     */
    useNullAsDefault?: Config['useNullAsDefault'];
  };
}

Decorators are incombatible with typescript and follow a legacy standard

As discussed on the phone, the decorators used in the controllers are following a legacy standard and are not compatible with typescript. The active version of the Decorators proposal only supports decorators on classes – not within object literals as Dito uses in the collection and member object literals.

One solution discussed was be to use a helper function to create the actions:

export class MyModels extends ModelController {
  modelClass = MyModel;

  collection = {
    allow: ['find', 'helloCollection'],
    index: createAction({
      action: ['get', '.'],
      returns: { type: 'string' },
      handler: () => 'Hello from the index action. Note: its method name does not matter.',
    }),
    helloCollection: createAction({
      parameters: [
        {
          name: 'msg',
          type: 'string',
          required: true,
        },
      ],
      handler: (msg) => `Model class '${this.modelClass.name}' says hello: ${msg}`,
    }),
  };
}

TypeError: Cannot read property 'messages' of null

I was able to reproduce the issue I mentioned in ab6e4e4 here: https://github.com/puckey/dito-example/tree/reproduce/tab-error

Go to /admin/dummies/1 and then click on the edit button of one of the messages.

it produces the following error:

vue.esm.js:628 [Vue warn]: Error in render: "TypeError: Cannot read property 'messages' of null"

found in

---> <DitoForm>... (1 recursive calls)
       <DitoView>
         <DitoRoot>
           <Root>
warn @ vue.esm.js:628

vue.esm.js:1897 TypeError: Cannot read property 'messages' of null
    at VueComponent.selectedTab (DitoSchema.vue:247)
    at Watcher.get (vue.esm.js:4488)
    at Watcher.evaluate (vue.esm.js:4593)
    at VueComponent.computedGetter [as selectedTab] (vue.esm.js:4845)
    at VueComponent.selectedTab (DitoForm.vue:129)
    at Watcher.get (vue.esm.js:4488)
    at Watcher.evaluate (vue.esm.js:4593)
    at Proxy.computedGetter (vue.esm.js:4845)
    at Proxy.pi (DitoForm.vue?3047:1)
    at VueComponent.Vue._render (vue.esm.js:3557)

Any guide for installation and setup?

It may seem newbie but I couldn't make it work so far. I'm a little confused, I don't know what should I do with those packages. Even example package didn't make much sense to me.

Anyway, I think this cool project needs some kind of getting started guide

clone() does not work with regular expression vaules.

This should do the trick:

function clone(val, iteratee = null) {
  let copy
  if (isDate(val)) {
    copy = new val.constructor(+val)
  } else if (isRegExp(val)) {
    copy = new RegExp(val)
  } else if (isObject(val)) {
    copy = new val.constructor()
    for (const key in val) {
      copy[key] = clone(val[key], iteratee)
    }
  } else if (isArray(val)) {
    copy = new val.constructor(val.length)
    for (let i = 0, l = val.length; i < l; i++) {
      copy[i] = clone(val[i], iteratee)
    }
  } else {
    copy = val
  }
  return iteratee?.(copy) ?? copy
}

isUrl fails for long tlds

We noticed that the isUrl utility function was failing for tlds longer than 6 characters. Added two failing tests to the following PR to demonstrate: 934e4af

Fix logger typings

the logger is now set up like this:

  setupLogger() {
    const { prettyPrint, ...options } = this.config.logger
    const transport = prettyPrint
      ? pino.transport({
          target: 'pino-pretty',
          options: prettyPrint
        })
      : null
    this.logger = pino(options, transport).child({ name: 'app' })
  }

We don't use koa-pino-logger or koa-logger anymore, jsut pino and pino-pretty, and app.config.logger gets split into { prettyPrint, ...options }, of which prettyPrint are the pino-pretty options, and …options are the pino options.

This should be reflected in the server typings, but I don't know enough to figure out how to do this.

@puckey could you take care of it? Thank you!

Logo design

Hi. I am a graphic designer. I volunteer to design a logo for open source projects. I can design it for you to use it in the readme file. What dou you say?

Fix errors in server typings

We have fixed most server typings, but one problem remains.

Running yarn types in the server package prints:

➜  server git:(master) ✗ yarn types
types/index.d.ts:1838:33 - error TS2536: Type '$Key' cannot be used to index type 'T'.

1838   [$Key in SelectModelKeys<T>]: T[$Key] extends Model
                                     ~~~~~~~

types/index.d.ts:1839:29 - error TS2536: Type '$Key' cannot be used to index type 'T'.

1839     ? SelectModelProperties<T[$Key]>
                                 ~~~~~~~

types/index.d.ts:1840:7 - error TS2536: Type '$Key' cannot be used to index type 'T'.

1840     : T[$Key]
           ~~~~~~~

This is all due to the following types:

export type SelectModelProperties<T> = {
  [$Key in SelectModelKeys<T>]: T[$Key] extends Model
    ? SelectModelProperties<T[$Key]>
    : T[$Key]
}

export type SelectModelKeys<T> = AnyGate<
  T,
  Exclude<
    keyof ConditionalExcept<T, Function>,
    |  `$${string}`
    | 'QueryBuilderType'
    | 'foreignKeyId'
  >,
  string
>

What I find interesting is that this all looks quite similar to what Objection.js does with ModelObject and DataPropertyNames.

I have played around with these and have managed to make the errors go away, but I am not certain if it's right:

type SelectModelKeys<T> = Exclude<
  objection.NonFunctionPropertyNames<T>,
  | `$${string}`
  | 'QueryBuilderType'
  | 'foreignKeyId'
>

@puckey could you take a look?

defaults in app configuration

  • env could default to process.env.NODE_ENV when not supplied
  • server.host could default to env.NODE_HOST || env.HOST || '0.0.0.0' when not supplied
  • server.port could default to env.NODE_PORT || env.PORT || 8080 when not supplied
  • log and server could be made optional like app is

Migrations always contain table.string('#id') and table.string('#ref')

When creating migrations, the tables always include #id and #ref columns:

export async function up(knex) {
  await knex.schema
    .createTable('channel', table => {
      table.increments('id').primary()
      table.string('#id')
      table.string('#ref')
    })
}

export async function down(knex) {
  await knex.schema
    .dropTableIfExists('channel')
}

Add support for transactions on controller actions

As proposed by @koskimas & @ollisal:

@action('get')
@transactional()
async doStuff(ctx) {
  await Thingy.query(ctx.trx)
  ...
}

The koa's middleware are perfect for transaction management:

async function transactionMiddleware(ctx, next) {
  const trx = ctx.trx = await transaction.start()
  try {
    await next()
    await trx.commit()
  } catch (err) {
    await trx.rollback()
  }
  ctx.trx = null
}

This would allow other middleware to use the transaction too. For example it's sometimes good (or even necessary) that the session update query takes part in the same transaction. This way the session middleware could use ctx.trx if it exists.

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.