Skip to content

Embedding one entity nested in another doesn't work correctly when using toHal() #80

Description

@sazzer

Embedding an entity that defines a toHal method on it inside another method with a toHal method on it works perfectly well, iff the embedding is done by use of configuration. If it is done using code in the toHal method of the outer entity then it does not work as expected.

The following works as expected:

function InnerModel(id, name, owner) {
    this.id = id;
    this.name = name;
    this.owner = owner;
}

InnerModel.prototype.toHal = function(rep, next) {
    rep.link('owner', '/users/' + this.owner.id);
    next();
}

function PageModel(items, totalCount) {
    this.items = items;
    this.totalCount = totalCount;
}

PageModel.prototype.toHal = function(rep, next) {
    rep.link('first', '?index=0'); // To prove that the toHal method runs
    next();
}

server.route({
    method: 'GET',
    path: '/items',
    config: {
        handler: (req, reply) => {
            reply(new PageModel([new InnerModel(1, 'Name', {id: 1001, name: 'Graham'})], 10));
        },
        tags: ['api'],
        plugins: {
            hal: {
                embedded: {
                    item: {
                        path: 'items',
                        href: './{item.id}'
                    }
                }
            }
        }
    }
});

And this produces the following output:

{
  "_links": {
    "self": {
      "href": "/items"
    },
    "first": {
      "href": "?index=0"
    }
  },
  "totalCount": 10,
  "_embedded": {
    "item": [
      {
        "_links": {
          "self": {
            "href": "/items/1"
          },
          "owner": {
            "href": "/users/1001"
          }
        },
        "id": 1,
        "name": "Name",
        "owner": {
          "id": 1001,
          "name": "Graham"
        }
      }
    ]
  }
}

However, if I try and do this with code instead, as follows:

function InnerModel(id, name, owner) {
    this.id = id;
    this.name = name;
    this.owner = owner;
}

InnerModel.prototype.toHal = function(rep, next) {
    rep.link('owner', '/users/' + this.owner.id);
    next();
}

function PageModel(items, totalCount) {
    this.items = items;
    this.totalCount = totalCount;
}

PageModel.prototype.toHal = function(rep, next) {
    this.items.forEach(function(i) {
        rep.embed('items', './' + i.id, i);
    });
    rep.ignore('items');
    rep.link('first', '?index=0'); // To prove that the toHal method runs
    next();
}

server.route({
    method: 'GET',
    path: '/items',
    config: {
        handler: (req, reply) => {
            reply(new PageModel([new InnerModel(1, 'Name', {id: 1001, name: 'Graham'})], 10));
        },
        tags: ['api'],
        plugins: {
            hal: {
            }
        }
    }
});

And this produces the following output:

{
  "_links": {
    "self": {
      "href": "/items"
    },
    "first": {
      "href": "?index=0"
    }
  },
  "totalCount": 10,
  "_embedded": {
    "item": [
      {
        "_links": {
          "self": {
            "href": "/items/1"
          }
        },
        "id": 1,
        "name": "Name",
        "owner": {
          "id": 1001,
          "name": "Graham"
        }
      }
    ]
  }
}

Note in this case the inner Item does not have a link of "owner" even though the actual InnerModel class is exactly the same in both cases. The only difference is the one embeds the items using the "embedded" configuration and the other does it using "rep.embed" in the toHal method.

Note that I wanted to do it this way so that I can have a single PageModel class that is used everywhere instead of repeating the configuration everywhere that I want to do this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions