Giter VIP home page Giter VIP logo

bookshelf-jsonapi-params's People

Contributors

alechirsch avatar dependabot[bot] avatar flmg avatar gabe-fastpay avatar jamesdixon avatar massimo-ua avatar sempostma avatar sl-ffx avatar stas-fastpay avatar stasuravich 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

Watchers

 avatar  avatar  avatar  avatar  avatar

bookshelf-jsonapi-params's Issues

How to work with hasMany

Hi!
Trying to use the plugin and can't understand what i'm doing wrong with hasMany.

export const Roles = pg.Model.extend({
  tableName: 'roles',
  users() {
    return this.hasMany(Users);
  }
});

export const Users = pg.Model.extend({
  tableName: 'users',
  roles() {
    return this.hasMany(Roles);
  }
});

await Users.forge().fetchJsonApi({
    fields: {
      users: ['id', 'firstName', 'lastName'],
    },
    include: ['roles']
  });

I got:

  error: column roles.user_id does not exist

1__npm_run_dev__node_

need better method for detecting a collection

The JSON API spec requires that requests for a single record are returned as an object. Alternately, requests for collections, even if the collection only contains a single records, should be returned as an array.

Unfortunately, there isn't a great way to detect whether data coming into the plugin needs to call fetch or fetchAll depending on whether the user is requesting a single record or collection. I tried a few hacky things looking at Model#attributes and the Model#query._statements, but alas I broke it a few times.

Need a better way to determine this. May just revert to a flag that should be passed to the method.

query in one transaction failed

Hello,

I am trying to use RLS system on my app.
for that some of my queries should be in a same transaction.

with fetch, fetchAll, I done it without issue
so after I see the code of the fetchJsonApi, I supposed that there is no issue with standard fetch option, like : {transacting: t}, it is supported.

However, it seems that work not well, I get :
unrecognized configuration parameter app.userid
when I try to make a find query with SET app.userid TO ${userId}

Casing (camelcase / snakecase conversion)

So my database uses snakecase, however I'm outputting camelcase with the jsonapi-mapper package. So when I do ?fields[users]=orgId, it does not work since it expects org_id. Is there an option for this, or could I change it plugin so I don't have to convert the case every time?

Multiple sort columns that mix complex and non-complex fields result in bad SQL

Say for example you make a call like this:

PetModel.forge().fetchJsonApi({
  sort: ['petOwner.age', 'id']
});

You would get an error from your SQL database. This is because when internals.buildSort calls formatColumnNames the iteration has undesired behavior. On the first iteration of formatColumnNames, line 630 of src/index.js correctly sets columnNames[0] to 'petOwner.age'. On the second iteration however, line 651 modifies all columnNames via Bookshelf's Model.format. The resulting columnNames are thus ['pet_owner.age', 'id'], which raises this error at the database level.

select "pets".* from "pets" left outer join "pet_owners" as "petOwner" on "pets"."pet_owner_id" = "petOwner"."id" order by "pet_owner"."age" asc, "pets"."id" asc - missing FROM-clause entry for table "pet_owner"

I am getting around this problem now on a forked version by switching line 651 to be:

columnNames[key] = _keys(this.format({ [value]: undefined }))[0];

instead of what it is now which incorrectly modifies all indexes of columnNames

columnNames = _keys(this.format(columns));

When I get the cycles I will likely modify internals.formatColumnNames to formulate the columnNames using a map instead of the forEach loop which modifies indexes of the existing array and write a test case for with a relationship like petOwner that has a name which would be affected by this issue.

LIKE results in ER_PARSE_ERR in MySQL (incorrect use of quotes)

I've been playing a little bit with the filtering feature which is in beta, using this module + https://github.com/kideh88/node-jsonapi-query-parser

For the most part, it seems to work pretty well, which is awesome ๐Ÿ‘

However, I'm having a problem using like.

E.g. A plain eq filter works just fine

/users?filter[email][email protected]

However if I change it to

/users?filter[like][email][email protected]

I get an error:

ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '."email") like LOWER('%@myaddress.com%')) limit 10' at line 1

The query that is being output is:

select count(distinct users.id) from users where (LOWER("users"."email") like LOWER('%@myaddress.com%'));

The problem is that whilst double quotes are correct syntax for postgres and sqlite3, this is not valid MySQL syntax. MySQL uses backticks.

There are some issues in knex around this:

The solution seems to be to use a private API, taken from knex/knex#723 (comment)

var formatter = new query.client.Formatter(); // Also this works too: new.knex.client.Formatter()
var columnName = formatter.columnize('member.name');

fetch({ require: true })

Hi guys, thanks for the wonderful plugin ๐Ÿ‘ !
I want to trigger the NotFoundError of bookshelf. I normally did this by adding { require: true } parameter. Now if I add this to the object that comes out of the node-jsonapi-query-parser, I can't check it conditionally with if (error instanceof User.NotFoundError) { (I can do a regex though). Is there a quick fix for this? Thanks again!

implement "OR" filtering

@alechirsch I know we had briefly discussed a possible API to handle the case when you want to filter on x OR y rather than x AND y.

Want to restart that discussion here and figure out an API?

Response is not JSON

Stack:

  • hapi
  • bookshelf
  • MySQL

controllers/driver.js

'use strict';
var server = require( '../server' );
var bookshelf = require( '../bookshelf' );

var Driver = bookshelf.Model.extend( {
    tableName: 'Drivers'
} );

exports.index = server.route(
    {
        method: 'GET',
        path: '/drivers',
        config: {
            handler: function( request, reply ){
                Driver
                    .forge()
                    .fetchJsonApi()
                    .then((drivers) => {
                        reply(drivers);
                    });
            }
        }
    }
);

My response/reply is:

[
  {
    "id": "1",
    "first_name": "Name",
    "last_name": "Name",
    "license_number": "123456",
    "createdAt": "2016-07-05T16:00:00.000Z",
    "updatedAt": "2016-07-05T16:00:00.000Z"
  },
  {
    "id": "2",
    "first_name": "Name",
    "last_name": "Name",
    "license_number": "123456",
    "createdAt": "2016-07-05T16:00:00.000Z",
    "updatedAt": "2016-07-05T16:00:00.000Z"
  }
]

Expecting the data object/array.

I fully accept that I'm probably doing something wrong, but the only thing I feel like I'm doing differently is a reply() instead of return. Any thoughts?

Bug/ignore aggregate function format

#59

Format the columns of aggregate function not the entire expression.

For example with the case-converter-plugin:

First:
count(personId) -> count_person_id

Now:
count(personId) -> count(person_id)

filtering on a 'hasMany' relationship with pagination results in less rows than expected

Hi,

We have a model that has a hasMany relation. For example:

const Tree = Bookshelf.Model.extend({
  ...,
});

const Forest = Bookshelf.Model.extend({
  trees: function() {
    return this.hasMany('Tree');
  },
  ...,
});

If we now make a query like this:

Forest.fetchJsonApi({
  filter: {
    'trees.name': 'maple',
  },
  page: {
    page: 1,
    pageSize: 12,
  },
});

we expect to get the first 12 forests that have maple trees inside them. In reality we get less than 12, as it seems that either:

  • first the rows are fetched and then the filter is applied or
  • the pageSize parameter refers to the number of trees and not to the number of forests as the join operation results in more rows being returned than there are forests due to the hasMany relationship.

Could you point us to a solution? We are willing to also contribute a PR, but would be grateful for some help.

Thanks in advance and best regards!

Filtering on related models

I'm running into a problem when trying to filter relations. In Bookshelf, I would normally run the following query which should hopefully help to illustrate what I'm trying to do:

await new Speaker().fetchAll({withRelated: [
	{
		"events": function(qb) {
			qb.where("format", "=", 1);
		}
	}
]});

Selecting fields for a relation seems to work in the following way:

/speakers?include=events&fields[events]=name

So I tried a similar approach for the above:

/speakers?include=events&filters[events][format]=1

But this doesn't work. I've also tried a few other things, but no luck so far. So I'm wondering if what I'm trying to do is even possible with this package?

It looks like this this has already been raised in the past, and as far as I can tell, a fix was applied (#16) โ€“ but I'm not sure if this is fact working (and whether the above syntax is correct).

Any help would be greatly appreciated as I'm a bit stuck! Let me know if you need me to provide any additional info :)

Usage with node-jsonapi-query-parser

You recommend using the node-jsonapi-query-parser module and that the produced object can be passed directly into the plugin without modification; however, when attempting to do so, nothing whatsoever happens... no filtering, nada. Any ideas? Your help is greatly appreciated!

support filtering on relation fields

Currently, the plugin only supports filtering on the top level model. For example, for a person that has many pets, you can only sort on fields that belong to person. If you wanted to find only people that have dogs, you cannot do something like GET /people?filter[pet.species]=dog.

allow overriding of include parameters

When using the include parameter, the plugin currently creates a withRelated array based on the values of the parameter and passes it to fetch/fetchAll. In some cases, you may want to do something more advanced by passing a query builder expression.

We can check the options hash to determine if a withRelated function already exists for the included relation and use it rather than adding a generic param.

Sort descending only ever works 1 time

We've been experiencing issues with sort for a while, and haven't been able to figure out what was wrong.

Yesterday, we ran some tests, and determined the behaviour is as follows:

If you use sort descending, e.g. sort: ['-id'], it works the first time you run the query, and therefore passes tests / appears to work. However all subsequent requests go back to sorting by ascended.

I believe the culprit is the line of code value = value.substr(1);

In context:

// Loop through each sort value
_forEach(sortValues, (value) => {
    // If the sort value is descending, remove the dash
    if (value.indexOf('-') === 0){
        value = value.substr(1);
    }
    // Add relations to the relationHash
    internals.buildDependenciesHelper(value, relationHash);
});

If the sort value has a - at the start, it is overwritten without it.

I think this is a pass-by-reference error, where the option is getting overwritten so that it no longer has the - on subsequent passes.

Not 100% sure if this is the culprit, but the behaviour indicates that something like this is happening.

Filter using like operator

Hi, is the package currently support like operator statement, e.g., name like %john%.

Currently, I define the like operator as contains, so the URL would become like

/users?filter[email][contains]='gmail'

Then, I separated out that filter and build where statements manually based on that (I build a bookshelf plugin for this purpose). It would be really great if it is actually supported by bookshelf-jsonapi-params directly.

No handling of null values in filter

Pretty sure the issue is with this repo, rather than https://github.com/kideh88/node-jsonapi-query-parser.

Try to filter for null values doesn't work. If I try to set something like filter[deleted_at]=null in the query string, I get an empty result set.

If I try to set the filter programmatically before calling bookshelf-jsonapi-params, e.g.

options.filter = {
    deleted_at: null
};

I get this error:

TypeError: Cannot read property 'toString' of null
    at /Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/dist/index.js:385:50
    at /Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/node_modules/lodash/lodash.js:4411:15
    at baseForOwn (/Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/node_modules/lodash/lodash.js:2654:24)
    at /Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/node_modules/lodash/lodash.js:4380:18
    at forEach (/Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/node_modules/lodash/lodash.js:8634:14)
    at QueryBuilder.<anonymous> (/Users/hannah/Ghost/org/Daisy.js/node_modules/express-bookshelf-jsonapi/node_modules/bookshelf-jsonapi-params/dist/index.js:310:41)
    at Object.query (/Users/hannah/Ghost/org/Daisy.js/node_modules/bookshelf/lib/helpers.js:56:14)
    at query (/Users/hannah/Ghost/org/Daisy.js/node_modules/bookshelf/lib/model.js:1206:30)

Should this work? is there a special syntax that I should use?

Filter query always returns first result

A little stuck here. Getting ready to go live with my API using MySQL and I noticed that the filter query always returns the first result in the collection. Any thoughts?

Support 'like' filter on postgresql jsonb columns

Hey, great plugin!

I'm trying to use the like filter but it doesn't work on postgresql jsonb columns

select "mytable".* from "mytable" where (LOWER("mytable"."jsoncolumn") like LOWER($1)) - function lower(jsonb) does not exist

The request is generated by:

qbWhere.where(
    Bookshelf.knex.raw(`LOWER(${formatedKey}) like LOWER(?), [`%${typeValue}%`])
);

In postgresql, adding a ::text does the job:

select "mytable".*" from "mytable" where LOWER(("mytable"."jsoncolumn")::text) like LOWER('%test%');

Is there a way this could become a feature?

Or filtering does not respect the AND filters

Given the below filter:

{
  active: true,
  or: [
   {
     account_id: 1
   },
   {
     group_id: 5
   }
  ]
}

Currently the generated SQL for the where clause will produce this:

where
  active=true
  or ( account_id=1) or (group_id=5)

The generated SQL should instead look like this:

where
  active=true
  and ( ( account_id=1) or (group_id=5) ) 

Related to #63

@massimo-ua I found this when trying to use the or filter feature, I will be looking into a fix for this. Can you confirm your intentions when building out the feature? I do not want to break anything for you. Everything outside of the or array should be and'ed together.

support sorting on relation fields

Currently, the plugin only supports sorting on the top level model. For example, for a person that has many pets, you can only sort on fields that belong to person. If for some reason you wanted to sort person by the weight of their pets, you cannot do something like GET /people?sort=pet.weight.

See #14 for a deeper discussion.

add more comprehensive tests

Although there is a basic test suite in place, it doesn't test combinations of different types of parameters. For example, there should be tests to cover requests like: GET /customers/1?include=pets&fields[pet]=name where sparse fieldsets are applied to included relationships.

How to sort with `NULLS LAST` clause?

As we have some data in nested JSONB columns, we use the additionalQuery to add select and ordering for these columns, we need to do this manually is because we need put null values last, which (afaik) is not possible with the JsonApiParams. However, this introduces certain complications, such as also needing to inject your own select clauses for the columns we want to sort on.

Is there a way we can define to sort the nulls last?

Implement json filtering

As I see this plugin doens't support filitering columns with datatype json. Latest versions of postgres support json data type columns so I think it would be nice if this plugin also supported filtering for columns with data type json.

A sample of filtering would be
filter[column_json][property_1]=example
or
filter[like][column_json][property_1]=exam

associations and custom schema generates wrong query

Hi,

The bookshelf-jsonapi-params generates a query which failes on postgres. Maybe it's a special case, because we are using the postgres schema.
The problem is on these lines. The joinTableName also contains the schema. Because the alias ("as") does not distinguish the schema it interprets the name as "roles_custom.roles_users". But the third argument distinguish the schema and interprets the name as "roles_custom"."roles_users"."role_id".
That's why postgres throws the error missing FROM-clause entry for table "roles_users".

My question: Is the alias in the join statement needed?
If I remove the aliases the code works as expected.

Here is an example to reproduce

const knex = require('knex')
const Bookshelf = require('bookshelf')
const bookshelfJsonApiParams = require('bookshelf-jsonapi-params')

const db = knex({
  client: 'pg',
  connection: {
    database: 'test',
    host: 'localhost',
    password: 'test',
    port: 5432,
    user: 'test'
  }
})

const bookshelf = new Bookshelf(db)

bookshelf.plugin(bookshelfJsonApiParams, {
  pagination: {
    limit: 25
  }
})

class Roles extends bookshelf.Model {
  get tableName() {
    return 'custom.roles'
  }

  users() {
    return this.belongsToMany('User', 'custom.roles_users', 'user_id', 'role_id')
  }
}

class Users extends bookshelf.Model {
  get tableName() {
    return 'custom.users'
  }

  roles() {
    return this.belongsToMany('Role', 'custom.roles_users', 'role_id', 'user_id')
  }
}

bookshelf.model('Role', Roles)
const User = bookshelf.model('User', Users)

new User()
  .fetchJsonApi({
    filter: {
      'roles.id': '1'
    },
    include: ['roles']
  })
  .then(console.log)
  .catch(console.error)

Here is the full error

error: select "custom"."users".* from "custom"."users" left outer join "custom"."roles_users" as "roles_custom.roles_users" on "custom"."users"."id" = "roles_custom"."roles_users"."role_id" left outer join "custom"."roles" as "roles" on "roles_custom"."roles_users"."user_id" = "roles"."id" where "roles"."id" in ($1) limit $2 - missing FROM-clause entry for table "roles_users"
    at Parser.parseErrorMessage (/Users/operator/Projects/redbull/canvases-api/node_modules/pg-protocol/dist/parser.js:287:98)
    at Parser.handlePacket (/Users/operator/Projects/redbull/canvases-api/node_modules/pg-protocol/dist/parser.js:126:29)
    at Parser.parse (/Users/operator/Projects/redbull/canvases-api/node_modules/pg-protocol/dist/parser.js:39:38)
    at Socket.<anonymous> (/Users/operator/Projects/redbull/canvases-api/node_modules/pg-protocol/dist/index.js:11:42)
    at Socket.emit (events.js:315:20)
    at addChunk (internal/streams/readable.js:309:12)
    at readableAddChunk (internal/streams/readable.js:284:9)
    at Socket.Readable.push (internal/streams/readable.js:223:10)
    at TCP.onStreamRead (internal/stream_base_commons.js:188:23) {
  length: 123,
  severity: 'ERROR',
  code: '42P01',
  detail: undefined,
  hint: undefined,
  position: '145',
  internalPosition: undefined,
  internalQuery: undefined,
  where: undefined,
  schema: undefined,
  table: undefined,
  column: undefined,
  dataType: undefined,
  constraint: undefined,
  file: 'parse_relation.c',
  line: '3301',
  routine: 'errorMissingRTE'
}

Descending sort arguments are getting incorrectly transformed

We are seeing invalid results when passing in a descending sort argument. Basically it ignores this part of the JSON API Spec: 'The sort order for each sort field MUST be ascending unless it is prefixed with a minus (U+002D HYPHEN-MINUS, โ€œ-โ€œ), in which case it MUST be descending.' Sort arguments prefixed with a minus are not being properly processed.

We think you could resolve it in the buildSort function. You could to check for sort params passed with a minus (meaning descending sort), store the sort direction, remove the minus before formatting the arguments and call internals.model.orderBy() with the secondary optional argument supplying the sort direction you saved earlier.

Unable to page using page[number] and page[size]

Currently the package allows for page[limit] and page[offset], however it does not work with page[number] and page[size]. I took a look at the 'bookshelf-page' package and found that they use 'page' and 'pageSize', however these parameters do not seem to work either. This is probably an issue with the 'bookshelf-page' package rather than this package.

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.