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

improvements to operation middleware #86

Merged
merged 13 commits into from Dec 1, 2020

Conversation

tpluscode
Copy link
Contributor

No description provided.

@codecov-io
Copy link

codecov-io commented Oct 27, 2020

Codecov Report

Merging #86 (81227db) into master (8597063) will increase coverage by 0.45%.
The diff coverage is 98.38%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #86      +/-   ##
==========================================
+ Coverage   94.80%   95.25%   +0.45%     
==========================================
  Files          11       11              
  Lines         250      274      +24     
==========================================
+ Hits          237      261      +24     
  Misses         13       13              
Impacted Files Coverage Δ
StoreResourceLoader.js 94.73% <ø> (ø)
lib/middleware/resource.js 90.90% <85.71%> (-1.95%) ⬇️
lib/middleware/operation.js 100.00% <100.00%> (ø)
middleware.js 89.79% <100.00%> (+0.21%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8597063...81227db. Read the comment docs.

@tpluscode tpluscode requested a review from bergos October 28, 2020 07:57
Comment on lines +85 to +94
<Category> a hydra:Class ;
hydra:supportedOperation
<category#get>;
hydra:supportedProperty [
hydra:property <post>
] ;
hydra:supportedProperty [
hydra:property <pinned-post>
]
.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added Category class with those two properties to test the behavior of resources only used as objects of those properties

@@ -0,0 +1,3 @@
<http://localhost:9000/category/csvw> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://localhost:9000/api/schema/Category> .

<http://localhost:9000/category/csvw> <http://localhost:9000/api/schema/post> <http://localhost:9000/post/2> .
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

post/2 used only once here, will 404

@@ -0,0 +1,3 @@
<http://localhost:9000/category/rdf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://localhost:9000/api/schema/Category> .

<http://localhost:9000/category/rdf> <http://localhost:9000/api/schema/post> <http://localhost:9000/post/3> .
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

post/3 used with two properties. Will give 405 when requested with a method supported by neither property

middleware.js Outdated
router.use(waitFor(init, () => middleware.resource))
}
router.use(waitFor(init, () => operation(api)))
router.use(waitFor(init, () => operation(api, middleware.resource)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the resource middleware inside the operation because the resource handler above may not set a single req.hydra.resource.

if (resources.length > 1) {
return next(new Error(`no unique resource found for: <${req.hydra.term.value}>`))
debug('Multiple resource candidates found')
return next()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not error immediately but instead set the potential "property operation" candidates to be filtered by the operation middleware

Comment on lines 54 to 56
return clownface({
_context: [...matched._context, more._context],
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct way to merge multiple clownface contexts?

I use this way to combine all potential matching operations for the property candidates

Comment on lines 7 to 9
if (resources.length > 1) {
return next(new Error(`no unique resource found for: <${req.hydra.term.value}>`))
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is moved up because only "multiple class resources" are an issue.

Multiple candidates for property operation can still be reconciled

@tpluscode tpluscode marked this pull request as ready for review November 27, 2020 10:53
Comment on lines +119 to +129
if (middleware.resource) {
router.use(middleware.resource)
}
router.use(invokeOperation)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned about this part. The middleware.resource has to be aware that the req.hydra.resource might be undefined if no resource+operation pair was selected.

It would make sense to move it inside invokeOperation handler but because it is also an express middleware then the result might be a little awkward.

Comment on lines 94 to 95
if (operationMiddleware) {
operations = operationMiddleware(operations, { req, res })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the operations middleware hook every time. This way an API might get a chance to select an operation even if none were found otherwise. Or replace those found with something else completely.

Comment on lines 64 to 66
if (operationMiddleware) {
operations = operationMiddleware(operations, { req, res })
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the operations middleware hook every time. This way an API might get a chance to select an operation even if none were found otherwise. Or replace those found with something else completely.

Comment on lines +119 to +127
if (middleware.resource) {
router.use(middleware.resource)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned about this. The middleware.resource must be aware that req.hydra.resource can be undefined if no resource+operation pair was indeed found.

Maybe move it inside invokeOperation somehow?

}

return [{ resource, operation: null }]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeps the resources without any operations around to later determine if they are subject or object usage

@tpluscode tpluscode changed the title failing cases for property operations improvements to operation middleware Nov 27, 2020

if (operations.length > 1) {
error('Multiple operations found')
return next(new Error('Ambiguous operation'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but maybe we could use 409 Conflict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

400 range is intended for client errors. Failure to find the right operations is 99% going to be a server issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of the time it would be a missing or unclear rdf:type in a request body. But maybe it's better that the custom middleware explicitly sets that status code for such cases.

const { api } = req.hydra

if (!req.hydra.operation) {
const operationMap = mapOperations({ api, res })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapOperations does not call operationMiddleware, if given. For the edge case that an additional operation would be added with a different method, that method would not be included in the allowed list. I'm not sure if it needs to be covered. An additional operation would mean it was not discovered using the API description, so it's anyway somehow hidden. But it could be also the case, that we need it for inline operations, once we add that feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core benefit I'm looking for right now is ability to disambiguate operations if multiple are found.

While this also allows adding/replacing operations, I would say that one does on their own responsibility

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep it like this now, but in a separate PR we should have a look at all the edge cases.

@@ -2,26 +2,21 @@ const { debug } = require('../log')('resource')

function factory ({ loader }) {
return async (req, res, next) => {
let resources = await loader.forClassOperation(req.hydra.term)
const classResources = await loader.forClassOperation(req.hydra.term)
const propertyObjectResources = await loader.forPropertyOperation(req.hydra.term)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search for property operations can be more expensive on some stores/loaders. Maybe we can add an option to skip the property operation search if there is already a class operation. Just a remark for a possible future PR. This should not be covered in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's how it was before pretty much. I would probably propose a separate PR to actually combine the resource.js and operation.js seeing how the former shrunk and only really calls the loader. This way should be easier to implement this again elegantly

let operations = mapOperations({ api: req.hydra.api, res, method })
.filter(({ operation }) => operation)

if (operationMiddleware) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The operationMiddleware is not a typical express middleware. I think that could be confusing. Last time we discussed it, I tended to use a more focused callback interface. Now that I have again a look at the code and see the existing middleware.resource, I would prefer changing it to a normal express middlware. That means wrapping it with Router to support all kinds of middlware arguments (plain function and arrays). operations could be attached to req.hydra.

const { api } = req.hydra

if (!req.hydra.operation) {
const operationMap = mapOperations({ api, res })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep it like this now, but in a separate PR we should have a look at all the edge cases.


if (operations.length > 1) {
error('Multiple operations found')
return next(new Error('Ambiguous operation'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of the time it would be a missing or unclear rdf:type in a request body. But maybe it's better that the custom middleware explicitly sets that status code for such cases.

@tpluscode tpluscode merged commit 14cdcee into master Dec 1, 2020
@tpluscode tpluscode deleted the property-operations-responses branch December 1, 2020 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants