Giter VIP home page Giter VIP logo

schwifty's People

Contributors

damusix avatar devinivy avatar mattboutet avatar nargonath avatar rickerd avatar samueljoli avatar tmbarkve avatar wswoodruff avatar zemccartney 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

Watchers

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

schwifty's Issues

Proposed plugin API changes for v6

Currently schwifty and schmervice have similar interfaces to each other, but they aren't as in-sync as they could be. For example:

  • In schwifty you can register models while registering the plugin itself, but you can't register services that way with schmervice.
  • Schwifty has additional configuration that schmervice does not, so server.schwifty() does double-duty to handle that in addition to registering models. Schmervice's server.registerService() only registers services.
  • As a result, their decoration naming is not perfectly parallel: schwifty() versus registerService().

I would like to bring these closer in line with each other. I would propose that registering models should have a dedicated server decoration just for that purpose. Rather than keeping server.schwifty() for additional configuration (e.g. server.schwifty({ knex, migrationsDir }), I propose this configuration should always be specified by registering schwifty. There are two upsides to that approach: 1. it encourages all plugins that depend on schwifty to go ahead and register it themselves, and 2. it reduces duplication in the surface area of the API that currently exists between server.schwifty() and schwifty's plugin options. By the same token, the models option that currently may be provided as a plugin option would be removed.

If you want to declare a knex instance and register a model that will be bound with it then there would be exactly one way to write it (there are currently three!):

await server.register({
    plugin: require('schwifty'),
    options: {
        knex: { client: 'sqlite3' }
    }
});

server.registerModel(UserModel);

I started getting into the question: are the decorations models() and registerModel() too generic? What if another plugin on the user's server also wants to manage models of some sort? I realized that we could deal with that that by allowing the user to opt-in to namespacing these decorations to something like server.pal.models() or server.schwifty.models(), so we don't need to take this into account at the moment.

I am pretty happy with the rest of the plugin API now that namespacing and sandboxing have landed (#84). That's all I've got for now. Please feel free to weigh-in and let us know what you think!

Update to use objection v0.7's validators

Objection is codifying the idea of "validators." This is good for us, since we're using a non-default validation library (joi!). Currently v0.7 is only in release-candidate phase, but when it comes out we'll want to make updates (shouldn't be giant!) and release the next version of schwifty. We don't know exactly how it's going to timeout in relation to the release of schwifty v1, so let's not worry about it too much.

Let's just keep an eye on this for now, https://github.com/Vincit/objection.js/releases
Validator reference, Vincit/objection.js@b553097

Allow models to opt out of knex binding

We will introduce a symbol Schwifty.bindKnex that may be placed on a model (i.e. staticly) that, when set to false, will opt the model out of being bound to the plugin's knex instance. This will allow for smoother multi-tenant models on multi-plugin servers.

Support models() to returning a single model by name

I wonder if it would be convenient if request.models() and server.models() could be used as such,

server.models('Movie');     // Returns Movie class
server.models('Dog');       // Returns null, since Dog is defined in another plugin.  Or throws hard??
server.models('Dog', true); // Returns Dog class defined in another plugin

How to use Schwifty models in a seed file?

First off, compliments on all of the excellent hapi pal libraries. I have really enjoyed using them so far. I am hoping to build a simple seed file using my project's Schwifty models. My project is set up according to the hapi pal docs boilerplate.

I can use the knex cli to make a seed file, but am not sure how to use my Schwifty models inside of a knex seed file, and if I just want to do all seeding via Schwifty models, I am not sure I need the knex seed boilerplate.

I attempted to manually create a seed.js file at my project root, trying to access the models from the server object, similar to how I imagine it works with hpal-debug. I was seeing a knex error when attempting to construct a simple query:

const server = require('./server');

server.deployment().then(serverInstance => {
  const models = serverInstance.models()

  const { Users } = models
  const users = Users.query().then(users => {
    console.log(users)
  })
})

resulted in the following error:
Error: no database connection available for a query. You need to bind the model class or the query to a knex instance.

I seem to be missing a step to provide the models with a knex instance, but was thinking that step already happened during the server setup. I can see that the serverInstance object returns a knex instance if I call serverInstance.knex()and returns the expected models if I call serverInstance.models(). I am not sure what the missing step is here?

Model/knex namespacing and sandboxing

See also hapipal/schmervice#10 where similar features are implemented for schmervice.

Namespaces are an extension to the ability to pass true to server.models(true) in order to access all models (from the perspective of the root server), and similarly for server.knex(true). Now you may also pass a plugin name as a string server.models('my-plugin') (and server.knex('my-plugin')) in order to access models/knex from the perspective of that plugin. In other words, calling server.models() inside my-plugin is the same as calling server.models('my-plugin') from any plugin; identically calling server.models() on the root server is the same as calling server.models(true) from any plugin.

Sandboxing is a mechanism to opt-out of models being available to all ancestor plugins/namespaces of the plugin that registered the model, and same goes with knex instances for child plugins/namespaces. This essentially is the schwifty-y way of having models and knex instances that are "private" within the plugin that registered them.

These features work together to enable better plugin encapsulation and greater testability, with the ability to not only access, but also mock-out models on a per-plugin basis.

Take advantage of Joi's `meta` ability to specify `columnName` for a property

This feature will allow Schwifty to retro-fit onto an existing database with strange or ugly column names, preserving your right to have sensible prop names that're easy on the eyes.

When writing a joiSchema, be able to specify the database's columnName that maps to this property like so:

{
    fieldName: Joi.string().meta({
        columnName: 'field_name'
    })
}

Preserve model names when binding to knex

Objection uses AnonymousModelSubclass as the class name for models returned from Model.bindKnex(). For debuggability, we should instead preserve the original model class names.

jsonAttributes causing errors on server start

Where:
This is happening when trying to start the server with schwifty's master branch, and the add-schwifty branch of user-boilerplate: https://github.com/WilliamSWoodruff/user-boilerplate/tree/add-schwifty.

Repro:

  • You will not need a working db username, login, or db name, this error shows up before that.
  • Clone both repos, npm link schwifty into user-boilerplate, run npm install on both and you'll see this stacktrace

Stacktrace:

/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:809
      this.jsonAttributes = [];
                          ^

TypeError: Cannot set property jsonAttributes of class SchwiftyModel extends Objection.Model {

    static get joiSchema() {}

    // Caches schema, with and wi...<omitted>...
} which has only a getter
    at Function.getJsonAttributes (/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:809:27)
    at Tokens.$parseDatabaseJson (/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:297:37)
    at Function.columnNameToPropertyName (/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/ModelBase.js:601:50)
    at Function.decorator$memoize [as columnNameToPropertyName] (/Users/myUser/Developer/schwifty/node_modules/objection/lib/utils/decorators/memoize.js:63:24)
    at /Users/myUser/Developer/schwifty/node_modules/objection/lib/relations/Relation.js:565:37
    at Array.map (native)
    at BelongsToOneRelation.propertyName (/Users/myUser/Developer/schwifty/node_modules/objection/lib/relations/Relation.js:564:20)
    at BelongsToOneRelation.setMapping (/Users/myUser/Developer/schwifty/node_modules/objection/lib/relations/Relation.js:272:27)
    at /Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:712:33
    at /Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:935:11
    at /Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:4944:15
    at baseForOwn (/Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:3001:24)
    at /Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:4913:18
    at baseReduce (/Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:932:5)
    at Function.reduce (/Users/myUser/Developer/schwifty/node_modules/lodash/lodash.js:9698:14)
    at Function.getRelations (/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:710:36)
    at Function.bindKnex (/Users/myUser/Developer/schwifty/node_modules/objection/lib/model/Model.js:567:32)
    at models.forEach (/Users/myUser/Developer/schwifty/lib/index.js:107:56)
    at Array.forEach (native)
    at collector.knexGroups.forEach (/Users/myUser/Developer/schwifty/lib/index.js:103:16)
    at Array.forEach (native)
    at internals.initialize (/Users/myUser/Developer/schwifty/lib/index.js:98:26)

Drop support for static models configuration

When importing schwifty in webpack, I get the following warning:

WARNING in ./node_modules/Schwifty/lib/index.js 94:28-50
Critical dependency: the request of a dependency is an expression

WARNING in ./node_modules/Schwifty/lib/index.js 98:28-76
Critical dependency: the request of a dependency is an expression

See webpack/webpack#196. There are some ways to get rid of these warnings in webpack, but it would be even better to avoid them in the first place.

Question: How to reduce coupling

Hi,

I generate my schwifty models from database using code generation tool. Tool generates Joi schema as well. I use model's joi schema as a base for validation in routes.

Overly simplified example:
routes/users.js

const UserModel = require("../models").User; // This causes coupling to model file

module.exports = {
  method: "post",
  path: "/users",
  config: {
    validate: { payload: UserModel.joiSchema },
    response: {
      schema: UserModel.responseSchema, // based on static get joiSchema()
    },
  },
  handler: async request => {
    const { User } = request.models();
    return User.query();
  },
}

My principles here are:

  • schwifty greatly decreases coupling to model files via request.models()
  • Reusing joi schema in model and routes is good for DRY.

And my question is:
To reuse joi schema from model, I have to require model file and this causes coupling. Is there any way to avoid coupling while accessing static methods of classes? Perhaps a static method from schwifty to access models (of course not instances but classes)

Thanks in advance.

Joi.attempt(config, Schema.schwifty) validation fails in Jest tests

I'm getting a Joi validation error on configuration object inside the internals.schwifty when trying to create a server inside Jest tests. Everywhere else (including NODE_ENV=test npm start) the server is being created just fine. The error disappears if I don't export any models.

Details and steps to reproduce:

// package.json

"scripts": {
    "test:jest": "NODE_ENV=test jest",
}

In any Jest test file:

// example.test.js

const Glue = require('glue');
const Manifest = require('../server/config/manifest');

const createServer = Glue.compose(Manifest.get('/'));

test('some test', async () => {
  const server = await createServer;
});

the command npm run test:jest -- example.test.js fails with the following:

ValidationError: {
      "knex": {
        "debug": false,
        "client": "pg",
        "connection": {
          "host": "127.0.0.1",
          "user": "user",
          "password": "password",
          "database": "db_test"
        }
      },
      "models": [
        function Users() {\n    _classCallCheck(this, Users);\n\n    return _possibleConstructorReturn(this, _getPrototypeOf(Users).apply(this, arguments));\n  } [2]
      ],
      "migrationsDir": "/server/config/migrations",
      "value" [1]: -- missing --
    }
    
    [1] "value" must be a Function
    [2] "0" must be a class

      at Object.<anonymous>.exports.process (node_modules/joi/lib/errors.js:203:19)
      at Object.<anonymous>.internals.Alternatives._validateWithOptions (node_modules/joi/lib/types/any/index.js:764:31)
      at Object.<anonymous>.module.exports.internals.Any.root.validate (node_modules/joi/lib/index.js:147:23)
      at Object.<anonymous>.module.exports.internals.Any.root.attempt (node_modules/joi/lib/index.js:177:29)
      at Object.<anonymous>.internals.schwifty (node_modules/schwifty/lib/index.js:190:18)
      at Object.register (node_modules/schwifty/lib/index.js:79:19)
      at Object.<anonymous>.internals.Server.register (node_modules/hapi/lib/server.js:453:35)

but if I transform the test file into a plain JS script:

// example.js

const Glue = require('glue');
const Manifest = require('../server/config/manifest');

const createServer = Glue.compose(Manifest.get('/'));

(async () => {
  const server = await createServer;
  console.log(server);
})();

and run it with NODE_ENV=test node example.js everything works fine and I see the server object being printed to stdout without any errors.

The error also disappears if I export an empty array for models:

// /server/models/index.js

const Users = require('./Users');

module.exports = [
  // Users,
];

I'm using Schwifty 4.2.0

$relatedQuery

What am I doing wrong that I can't run .$relatedQuery('clients') on my DataCentre model?

I had no issues with objection.js before trying out schwifty.

Overriding model throws. How to override?

Hi,

I'm trying to follow suggested best practice Extending schwifty models across plugins. However I'm using haute-couture (hc) and hapipal boilerplate.

I put my model file in lib/models/user.js file, and hc registers my user model.

Then following best practice, conditional below throws exception Model "User" has already been registered., because hc registered it.

server.schwifty(options.Model || UserModel);

What do you suggest to follow best practice using hc while keeping my user model in models directory.

Thanks,

Register multiple knex databases

Is there a way for Schwifty to handle multiple database connections while using knex plugin? I am looking for a way to connect to 2 different DB's so as i can define 2 different DB hosts.
Here is how i have added the very first entry ->

{ plugin: Schwifty, options: [{ knex: { client: 'postgresql', connection: { user: process.env.AWS_DB_USER, host: process.env.AWS_DB_HOST, database: process.env.AWS_DB, password: process.env.AWS_DB_PASSWORD, port: process.env.AWS_DB_PORT }, pool: { min: 2, max: 10 } } }] }

Lean on objection to preserve class names

Originally we hacked around objection to preserve model class names, but now it's no longer necessary– in objection v0.7.6 they started preserving class names in Model.bindKnex(). With this change to schwifty, if you use objection v0.7.0-0.7.5 then class names will not be preserved, making this a breaking change.

No knexfile?

Hi!

I'm new to Hapi and try to setup a new project with knex & objection, this plugin seems to be really cool but I'm probably missing something: is it me or you don't need the generated knexfile to specify all the config informations? I prefer to use it as it's the original knex stuff and I find it reassuring :)

Can you enlight me?

Thanks!

possible to access models in tests?

Hello!

Is it possible to access objection models in tests using Schwifty?

I'm trying the following:

it('can retrieve an auth token with valid credentials', async () => {

            const Member = server.models().Member;

            const newMember = await Member.query().insert({});
            request.payload = {
                data: {
                    type: 'token',
                    attributes: {
                        email: '[email protected]',
                        password: 'Q1w2e3r4'
                    }
                }
            };

            const res = await server.inject(request);

            expect(res.statusCode).to.equal(401);
        });

When I log Member, it's undefined. If I log server.models(), it returns an empty object.

THanks!

Seeds dir for Knex

I'm building an API on Hapi pal with Haute Couture, Hoek, Knex, Objection and Schwifty all is fine but when i use the seedsDir it breaks Hapi itself. The server won't start.
Tho I am able to run migrations + seeds.

Maybe it's an idea to add this to the options? I've been looking into it but can't find where to add it. I'm not that skilled in Schwifty/node yet.

module.exports = {
    plugins: {
        options: {
            migrationsDir: `${__dirname}/../migrations`,
            seedsDir: `${__dirname}/../seeds`, // <== this is making problems
        }
    }
};

Provide migrations stub/template file

Knex 0.18.4+ allows the user to specify a different template for the output of knex migrate:make. The current template doesn't follow the hapi style guide or use async/await, so it would be useful for schwifty to fill the gap here by exposing a path to a more suitable stub file Schwifty.migrationsStubPath.

Directory structure

Hi!

Me again, sorry 😏 I've reading the doc again and again but still confused on what is the best practice to structure my project. For now I have an api folder with every models represented by a folder, containing the Model file, the routes etc... But as you register the models in the server file, how can I do it in each model.js file that I have?
I'm not sure I'm making myself clear, maybe a pic will help more:
capture d ecran 2017-11-23 a 10 41 21

Thanks a lot, again :)

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.