Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

todoList example not working as expected: ?missing route GET /todo-list/{id}/todos #2050

Closed
cloudwheels opened this issue Nov 18, 2018 · 12 comments · Fixed by #4412
Closed
Assignees
Labels
Docs Examples good first issue REST Issues related to @loopback/rest package and REST transport in general
Milestone

Comments

@cloudwheels
Copy link

cloudwheels commented Nov 18, 2018

Description / Steps to reproduce / Feature proposal

  • Follow todoList tutorial from working version of todos example
  • follow steps to test new api behaviour

Current Behavior

  • POST /todo-lists with a body of { "title": "grocery list" }

curl -X POST "https://lb4.eu-gb.mybluemix.net/todo-lists" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"title\":\"grocery list\"}"

200 OK
{
  "id": "f802a73624452ae109f5f67921f029b1",
  "title": "grocery list"
}
  • POST /todo-lists/{id}/todos using the ID you got back from the previous POST request and a body for a todo. Notice that response body you get back contains property todoListId with the ID from before.

curl -X POST "https://lb4.eu-gb.mybluemix.net/todo-lists/f802a73624452ae109f5f67921f029b1/todos" -H "accept: */*" -H "Content-Type: application/json" -d "{\"title\":\"my Todo\",\"desc\":\"todo added via todo-lists/f802a73624452ae109f5f67921f029b1/todos\"}"

200 OK
{
  "id": "89457642c79b2bd07c0d09c5e347d1d3",
  "title": "my Todo",
  "desc": "todo added via todo-lists/f802a73624452ae109f5f67921f029b1/todos",
  "todoListId": "f802a73624452ae109f5f67921f029b1"
}

Expected Behavior

As expected until this point.

  • GET /todos/{id}/todos and see if you get the todo you created from before.

  • GET /todos/{id}/todos is not a route!
    Should there be a route GET /**todo-list**/{id}/todos ??

  • GET /todos/{id} returns the todo:
    curl -X GET "https://lb4.eu-gb.mybluemix.net/todos/89457642c79b2bd07c0d09c5e347d1d3" -H "accept: application/json"

200 - OK
{
  "id": "89457642c79b2bd07c0d09c5e347d1d3",
  "title": "my Todo",
  "desc": "todo added via todo-lists/f802a73624452ae109f5f67921f029b1/todos",
  "todoListId": "f802a73624452ae109f5f67921f029b1"
}
  • The todoList Model is not updated with the todo (or reference to it) on its list:
    GET todo-lists/{id}
    curl -X GET "https://lb4.eu-gb.mybluemix.net/todo-lists/f802a73624452ae109f5f67921f029b1" -H "accept: application/json"
200 -OK
{
  "id": "f802a73624452ae109f5f67921f029b1",
  "title": "grocery list"
}

Expected: (minimum of)

{
  "id": "f802a73624452ae109f5f67921f029b1",
  "title": "grocery list",
 "todos": [
    {
      "id": "89457642c79b2bd07c0d09c5e347d1d3"
    }
  ]
}

@dhmlau FYI

@cloudwheels
Copy link
Author

cloudwheels commented Nov 18, 2018

I can implement GET /todo-list/{id}/todos as follows but still think I'm missing the point a bit here...

//src/todo-list-todo.controller.ts
//...
@get('/todo-lists/{id}/todos')
  async find(@param.path.string('id') id: string) {
    return await this.todoListRepo.todos(id).find({"where": {"todoListId": id}});
  }
//...

curl -X GET "https://lb4.eu-gb.mybluemix.net/todo-lists/f802a73624452ae109f5f67921f029b1/todos" -H "accept: */*"

200 - OK
[
  {
    "id": "89457642c79b2bd07c0d09c5e347d1d3",
    "title": "my Todo",
    "desc": "todo added via todo-lists/f802a73624452ae109f5f67921f029b1/todos",
    "todoListId": "f802a73624452ae109f5f67921f029b1"
  }
]

@cloudwheels
Copy link
Author

cloudwheels commented Nov 18, 2018

I have closed #2048 because it was a bit muddled but the points are still relevant:

  • To some extent, it is a matter of data structure design whether I want GET/todo-list/{id} to return:
    -- just 'header' information about the list, with related todos only accessible through GET /todo-list/{id}/todos
    -- this plus an array of todo ids, ids plus something like the title (for quick lists), or a full 'copy' of the todo objects : it is up to me to find a way to implement and manage updates I guess

What SHOULDN'T be possible (unless I am choosing to view todos as an array property of todoList and not its own object), is POST /todo-lists with a todo in the body, as this does not create the todo object and relations in the same way:
curl -X POST "https://lb4.eu-gb.mybluemix.net/todo-lists" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"title\":\"a list with todos added from /post/todo-lists\",\"todos\":[{\"title\":\"a todo added via /post/todo-lists\",\"desc\":\"a description\"}]}"

200 -OK
{
  "id": "9628859414b170c6d69005124b45c564",
  "title": "a list with todos added from /post/todo-lists",
  "todos": [
    {
      "title": "a todo added via /post/todo-lists",
      "desc": "a description"
    }
  ]
}

look for this todo..
curl -X GET "https://lb4.eu-gb.mybluemix.net/todos" -H "accept: application/json"

200 -OK - BUT NO RECORD FOR THE TODO - it only exists as a property on the todoList
[
  {
    "id": "89457642c79b2bd07c0d09c5e347d1d3",
    "title": "my Todo",
    "desc": "todo added via todo-lists/f802a73624452ae109f5f67921f029b1/todos",
    "todoListId": "f802a73624452ae109f5f67921f029b1"
  }
]

i.e. the constrained one-to-many repository is NOT READ ONLY as would be suggested by:

//src/repositories/todo-list.repository.ts
//...
public readonly todos: HasManyRepositoryFactory<
    Todo,
    typeof TodoList.prototype.id
  >;
//...

I have also tried setting the todos 'property' as 'protected' on the todoList model with no apparent affect (todos is of course actually a method / not decorated as a property so perhaps that's why) :

@model({
settings: {'protected': ['todos']}
})

@cloudwheels
Copy link
Author

Perhaps this is missing an implementation of belongsTo Relation?

Anyway, todo: #2049
The how to/concepts docs are focussed are largely on the shopping sample which I have now built successfully in CloudFoundry with Cloudant and Redis connections after the basic changes outlined in #2039 (ie Todos is just another implementation of the same concepts with more relevant names!) so personally I am exploring the concepts through that sample instead going forward.

@dhmlau
Copy link
Member

dhmlau commented Nov 20, 2018

@nabdelgadir, you were reviewing the todo-list example earlier. Could you please take a look? Thanks!

@nabdelgadir
Copy link
Contributor

GET /todos/{id}/todos and see if you get the todo you created from before.

GET /todos/{id}/todos is not a route!
Should there be a route GET /todo-list/{id}/todos ??

Yes, you're right. Thanks for catching that!

I can implement GET /todo-list/{id}/todos as follows but still think I'm missing the point a bit here...

//src/todo-list-todo.controller.ts
//...
@get('/todo-lists/{id}/todos')
async find(@param.path.string('id') id: string) {
return await this.todoListRepo.todos(id).find({"where": {"todoListId": id}});
}
//...

This is similar to this. You can specify the filter to be what you have: {"where": {"todoListId": id}}.

To some extent, it is a matter of data structure design whether I want GET/todo-list/{id} to return:
-- just 'header' information about the list, with related todos only accessible through GET /todo-list/{id}/todos
-- this plus an array of todo ids, ids plus something like the title (for quick lists), or a full 'copy' of the todo objects : it is up to me to find a way to implement and manage updates I guess

Perhaps this is missing an implementation of belongsTo Relation?

The correct way to do it is through using an include filter to retrieve the related model instances, but LoopBack 4 doesn't support it yet. However, we have an issue open for it.

@cloudwheels
Copy link
Author

Hi @nabdelgadir - thanks for the tips! It's a fast-moving project at the mo but getting to grips with what is/isn't/will be implemented as core functionality and where to extend myself. Aiming to get myself setup to chip in some small PRs like missing example bits to help things along when I have some time!

Interested in how the readonly (I guess exported from repository) and/or protected on model should be implemented to prevent POSTing directly to the todos on todoList rather than via the todoListTodos controller, as outline above.

@bajtos
Copy link
Member

bajtos commented Nov 22, 2018

Interested in how the readonly (I guess exported from repository) and/or protected on model should be implemented to prevent POSTing directly to the todos on todoList rather than via the todoListTodos controller, as outline above.

To prevent people from accessing /todos APIs, just delete the corresponding controller methods (e.g. TodoController.createTodo). To completely disable /todos endpoints, delete the entire TodoController from the project.

There is very little auto-magic in LB4 so far, almost everything is expressed in the code generated by lb4.

@cloudwheels
Copy link
Author

@bajtos no worries, that was my logical conclusion, but from a DX point of view, even trying hard to track the roadmap & 'what to expect' issues, one wonders if one is missing some 'Magic' :)

@bajtos
Copy link
Member

bajtos commented Nov 26, 2018

from a DX point of view, even trying hard to track the roadmap & 'what to expect' issues, one wonders if one is missing some 'Magic' :)

I am proposing to capture how to customize generated API in our docs. For example, add a new section called "Customizing generated REST API" to Controller generator.

Thoughts?

@CommerceOwl
Copy link

@bajtos I like the idea!

I was just going to log this issue. The tutorial mentions GET /todo-lists/{id}/todos and see if you get the todo you created from before.

It should be updated to include this discussion summary.

@bajtos bajtos added Examples Docs REST Issues related to @loopback/rest package and REST transport in general good first issue labels Jan 25, 2019
@dhmlau
Copy link
Member

dhmlau commented Jan 20, 2020

@nabdelgadir, not sure if your PR #4412 would fix it. Could you please take a look? Thanks!

@nabdelgadir
Copy link
Contributor

For the snippet:

{
  "id": "9628859414b170c6d69005124b45c564",
  "title": "a list with todos added from /post/todo-lists",
  "todos": [
    {
      "title": "a todo added via /post/todo-lists",
      "desc": "a description"
    }
  ]
}
  • You cannot currently create a todo through POST todo-lists/ as we reject navigational properties.
    • To create it for a specific todo-list, use the POST todo-lists/{id}/todos endpoint instead.
  • When you do GET todo-lists/, the todo-list returns won't include its todos by default. Instead, you need to use the filter: {include: [{relation: 'todos'}]} in the request.

When we land #4412, we can close this issue, but if you have any further questions, please let us know.

@dhmlau dhmlau added this to the Jan 2020 milestone Jan 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Docs Examples good first issue REST Issues related to @loopback/rest package and REST transport in general
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants