JKHeadley / rest-hapi

🚀 A RESTful API generator for Node.js
https://resthapi.com
MIT License
1.19k stars 153 forks source link

getAll request on '_MANY' association #191

Closed clegirar closed 5 years ago

clegirar commented 5 years ago

Describe the bug I want to get '_MANY' association and rest-hapi return all datas of the model of association

To Reproduce Steps to reproduce the behavior:

  1. Create '_MANY' association

    associations: {
    companies: {
        type: '_MANY',
        alias: 'companies',
        model: 'companies',
    },
    },
  2. I am creating companies in the model companies

    Capture d’écran 2019-08-22 à 18 07 04
  3. I post an id company in my companies association

    Capture d’écran 2019-08-22 à 18 19 53
  4. I get the association companies and response contains all the companies whereas in the companies association i have only one company

    {
    "docs": [
    {
      "_id": "5d5cae2e3356752c4f2d4414",
      "subsidiaries": [
        {
          "_id": "5d5cae373356752c4f2d4416",
          "subsidiaries": [],
          "name": "string",
          "naf": "string",
          "siret": "string",
          "createdBy": "5d5c9e1a6b233e246ad4f611",
          "createdAt": "2019-08-21T02:36:39.450Z",
          "institutions": []
        }
      ],
      "name": "string",
      "naf": "string",
      "siret": "string",
      "createdBy": "5d5c9e1a6b233e246ad4f611",
      "createdAt": "2019-08-21T02:36:30.548Z",
      "institutions": []
    },
    {
      "_id": "5d5cae373356752c4f2d4416",
      "subsidiaries": [],
      "name": "string",
      "naf": "string",
      "siret": "string",
      "createdBy": "5d5c9e1a6b233e246ad4f611",
      "createdAt": "2019-08-21T02:36:39.450Z",
      "institutions": []
    },
    {
      "_id": "5d5ebd0207b9b804f763dc2a",
      "subsidiaries": [],
      "name": "Hey",
      "naf": "string",
      "siret": "string",
      "createdBy": "5d5db71b1c99220acc90ae45",
      "createdAt": "2019-08-22T16:04:18.189Z",
      "institutions": []
    }
    ],
    "pages": {
    "current": 1,
    "prev": 0,
    "hasPrev": false,
    "next": 2,
    "hasNext": false,
    "total": null
    },
    "items": {
    "begin": null,
    "end": null,
    "total": 3
    }
    }

Expected behavior getAll request on '_MANY' association should return only the id's of the association and not fulld datas of the corresponding model.

Desktop (please complete the following information):

Additional context Sorry for my deplorable English, i hope its comprehensive ! Thanks for your answer.

JKHeadley commented 5 years ago

Hi @Clemssss, thanks for reaching out.

I believe I understand the issue you describe, though I will need some more information since I am unable to replicate it on my end.

1) Is this response from a REST endpoint (i.e GET /<your entity>/{ownerId}/companies) or from a wrapper method?

2) Could you display the JSON for the original parent object containing the _MANY relationship? (i.e. the result of GET /<your entity>/{_id})

Let's start from there and see if we can figure out what's going wrong. Thanks!

clegirar commented 5 years ago

Thank @JKHeadley for your speedy response !

  1. Yes is the response from the REST endpoint GET /<your entity>/{ownerId}/companies

  2. This is the response of GET /<your entity>/{_id} (companies is the _MANY relationship and she is embed), in itself this endpoint (GET /<your entity>/{_id}) return the _MANY relationship but if an endpoint is created just for this it would make more sense no ?

    {
    "_id": "5d5db71b1c99220acc90ae45",
    "companies": [
    {
      "_id": "5d5cae373356752c4f2d4416",
      "subsidiaries": [],
      "name": "string",
      "naf": "string",
      "siret": "string",
      "createdBy": "5d5c9e1a6b233e246ad4f611",
      "createdAt": "2019-08-21T02:36:39.450Z",
      "institutions": []
    }
    ],
    "email": "test@test.com",
    "password": "$2b$10$N4QlJTSrMfurtS3/U13bi.Dr0e2CxidEufFUrKT.PuEgY/DLFdaWW",
    "firstName": "string",
    "lastName": "string",
    "rights": {
    "user": true,
    "oppusUser": false
    },
    "createdAt": "2019-08-21T21:26:51.661Z"
    }

Once again thanks very much for your time and your reply! (PS: I would like to know what happened about this issue https://github.com/JKHeadley/rest-hapi/issues/149 ?)

JKHeadley commented 5 years ago

Hmm, yes GET /<your entity>/{ownerId}/companies should return the same documents as the embedded companies field. I'm not sure why it is not in your case. Would you be able to share some code (mainly model files) that will allow me to replicate the issue?

clegirar commented 5 years ago

Sure. UserSchema ()

const UserSchema = new Schema({
    firstName: { type: String, required: true },
    lastName: { type: String, required: true },
    phone: { type: String },
    email: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true },
    authyId: { type: String },
    companies: [{ type: Schema.Types.ObjectId, ref: 'company' }],
    rights: { type: Schema.Types.Mixed, default: {} },
    savedData: { type: Schema.Types.Mixed, default: {} },
});

module.exports = () => {
    UserSchema.statics = {
        collectionName: 'user',
        routeOptions: {
            associations: {
                companies: {
                    type: '_MANY',
                    alias: 'companies',
                    model: 'company',
                },
            },
            find: {
                pre: (_id, query, request) => {
                    const user = request.auth.credentials;
                    if (!_id || user._id !== _id) {
                        throw Boom.unauthorized('You are not authorized with this account.');
                    }
                    return {
                        $embed: ['companies'],
                    };
                },
            },
            list: {
                pre: (query, request) => {
                    const user = request.auth.credentials;
                    if (!user.rights.oppusUser) {
                        throw Boom.unauthorized('You are not authorized with this account.');
                    }
                    return {
                        $embed: ['companies'],
                    };
                },
            },
        },
    };
    return UserSchema;
};

CompanySchema

const CompanySchema = new Schema({
    name: { type: String, required: true },
    naf: { type: String, required: true },
    siret: { type: String, required: true },
    institutions: [{
        name: { type: String },
        address: {
            city: { type: String },
            street: { type: String },
            complementary: { type: String },
            zipCode: { type: String },
        },
        numberOfEmployees: { type: Number },
    }],
    subsidiaries: [{ type: Schema.Types.ObjectId, ref: 'company' }],
});

module.exports = () => {
    CompanySchema.statics = {
        collectionName: 'company',
        routeOptions: {
            associations: {
                subsidiaries: {
                    type: '_MANY',
                    alias: 'subsidiaries',
                    model: 'company',
                },
            },
            find: {
                pre: () => ({
                    $embed: ['subsidiaries'],
                }),
            },
            list: {
                pre: () => ({
                    $embed: ['subsidiaries'],
                }),
            },
        },
    };
    return CompanySchema;
};
clegirar commented 5 years ago

Ohh, i find, its the find and list pre-middlewares in the CompanySchema. If i delete it, it works, but i don't know why ...?

JKHeadley commented 5 years ago

Hi @Clemssss, it's because you are overriding the query parameter in the middleware function instead of modifying it. getAll internally uses the query parameter to filter the results based on the ownerId. When you override it, you erase the filter and therefore get all of the company documents rather than the docs under the ownerId.

Try changing your middleware to:

    find: {
      pre: (_id, query, request, Log) => ({
            ...query,
            $embed: ['subsidiaries']
          }),
    },
    list: {
      pre: (query, request, Log) => ({
            ...query,
            $embed: ['subsidiaries']
          }),
    },
clegirar commented 5 years ago

Exactly, im stupid ... Thanks for your response !

JKHeadley commented 5 years ago

Not at all, its easy to miss!