-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
fix: send 404 for findById(), if resource is not found #1617
Conversation
3401c63
to
ede1827
Compare
@hacksparrow Did you miss some files in the PR? I only see changes in tests. |
9c203fa
to
151f25b
Compare
151f25b
to
35880f3
Compare
@@ -35,7 +44,18 @@ export class TodoController { | |||
@param.path.number('id') id: number, | |||
@param.query.boolean('items') items?: boolean, | |||
): Promise<Todo> { | |||
return await this.todoRepo.findById(id); | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If legacy-juggler-bridge
throws the specific error instead of the generic Error
,
we won't need to try-catch
here.
However, there is a catch: legacy-juggler-bridge
needs to import @loopback/rest
,
which would create a cyclic dependency.
Maybe HttpErrors
should be a package of it's own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one's for fixing #1469.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does juggler throw errors with status
or statusCode
set for http? If so, we can check that pattern instead of forcing HttpErrors
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, HttpErrors
is from its own module. See https://github.com/strongloop/loopback-next/blob/3731d0f1ee18a12b36a6ec3785f295008b50d339/packages/rest/src/index.ts#L36. @loopback/rest
just re-export it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Juggler does not throw, it just returns null
. I am exploring the possibilities at Juggler level, because in another case it throws with code
and statusCode
.
@@ -128,7 +128,7 @@ export class DefaultCrudRepository<T extends Entity, ID> | |||
this.modelClass = dataSource.createModel<juggler.PersistedModelClass>( | |||
definition.name, | |||
properties, | |||
Object.assign({strict: true}, definition.settings), | |||
Object.assign({strict: true}, {strictDelete: true}, definition.settings), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes #1472.
@raymondfeng pushed two implementation changes. |
35880f3
to
3731d0f
Compare
@raymondfeng what's your comment on https://github.com/strongloop/loopback-next/pull/1617/files#r211287088? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are the delete tests related to #1469? I understand the demonstration but I'm guessing the fix will live in legacy-juggler
?
/* | ||
This is just a quick hack to demonstrate the problem. | ||
*/ | ||
if (e.message.match(/ound with id/)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
found
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a quick hack to demonstrate the problem.
😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should for a solution which requires no change in controllers.
d8afd85
to
a886344
Compare
This PR should land for this to work. |
a886344
to
d43b48f
Compare
Cross-posting my comment from loopbackio/loopback-datasource-juggler#1620:
A link to the solution we use in LoopBack 3.x: https://github.com/strongloop/loopback/blob/master/lib/persisted-model.js#L85-L95 In my opinion, the solution to this problem is to modify the generated CRUD Controller method Using our TodoController as an example: export class TodoController {
// ...
@get('/todos/{id}')
async findTodoById(
@param.path.number('id') id: number,
): Promise<Todo> {
const result = await this.todoRepo.findById(id);
if (result == null) {
throw new HttpError(
404,
`Todo #${id} was not found.`,
{code: 'MODEL_NOT_FOUND'}
);
}
return result;
}
} To avoid unnecessary repetition of the code handling "null to 404" conversion, we can extract a shared helper function into import {convertNullToNotFoundError} from '@loopback/rest';
export class TodoController {
// ...
@get('/todos/{id}')
async findTodoById(
@param.path.number('id') id: number,
): Promise<Todo> {
const result = await this.todoRepo.findById(id);
convertNullToNotFoundError(result);
return result;
}
} Thoughts? |
@virkt25 I have update the PR with only the relevant changes. |
@bajtos lovely solution! I will go ahead with it. |
@hacksparrow cool :) Maybe we can find a better name than |
@@ -210,12 +210,13 @@ export class DefaultCrudRepository<T extends Entity, ID> | |||
} | |||
|
|||
async findById(id: ID, filter?: Filter, options?: Options): Promise<T> { | |||
options = options || {}; | |||
// WIP: introducing `strictFind` to return `404` for non-existent resources. Currenyly returns `null` | |||
options.strictFind = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be configurable at the @model()
level similar to strict
and strictDelete
?
2f0ae48
to
8c5cf03
Compare
671c825
to
65a95d9
Compare
send 404 for findById(), if resource is not found
65a95d9
to
ad95035
Compare
Here is what is happening in the issue this PR is targeted at:
Making We could make Juggler throw This PR implements the solution at the controller level (to initiate the discussion). However, requiring to add the So the ideal solution should be at the framework-level but unexposed or barely-exposed to the user. Miroslav and I have been exploring various options but have not found a good solution yet. It would be great to hear from you all. There are the possible starting points:
With reference to legacy-juggler-bridge: Why is the return type of |
What does the error from juggler look like? I don’t think we can just convert all errors from Juggler to a 404 because what if the juggler didn’t find the model because of a database connection issue? My guess is that the reason findOne returns null is since it takes a filter for a search and returns the first item matching the criteria and nothing matching isn’t necessarily a NotFoundError but maybe it is? I think as pointed out, we should aim for consistency and the findByID method is inconsistent and should return null if no item exists (now we know there wasn’t a database error). In the controller we can introduce a helper function to return a 404 for *ByID methods if the result is undefined — which can live in the rest package.
Let’s see what others think before proceeding. |
If the model is not found, it returns/resolves |
This is exactly what we were trying for. But this requires change in
should become
To me, a model-querying promise resolving two different types seem illogical; and especially if it resolves |
@raymondfeng any suggestions? |
@hacksparrow After reading through this thread, I think the solutions suggested by @bajtos at #1617 (comment) and @virkt25 at #1617 (comment) are sensible. From the 3 starting points you listed, I don't think 2) and 3) are good approaches for the reasons you mentioned. |
I'm leaning toward |
Another option to consider:
IMO, the proposed API |
I went ahead and implemented my proposal in #1658. |
Closing in favor of #1658. |
For fixing
#1472 and#1469.Update: Since the causes of #1472 and #1469 are worlds apart. I will open a new PR for addressing #1472.