Skip to content

ACC-2100: add RestController for relations#39

Merged
NielsCW merged 32 commits intomainfrom
ACC-2100
Jul 4, 2025
Merged

ACC-2100: add RestController for relations#39
NielsCW merged 32 commits intomainfrom
ACC-2100

Conversation

@NielsCW
Copy link
Contributor

@NielsCW NielsCW commented Jun 5, 2025

Add RelationRequestHandler, it implements the following methods from PropertyRequestHandler and PropertyItemRequestHandler:

  • getProperty: GET /{entity}/{sourceId}/{relation}
    • redirect to target entity item if *-to-one relation
    • redirect to target entity collection if *-to-many relation (query parameters present but don't work)
  • putProperty: PUT /{entity}/{sourceId}/{relation}
    • set a *-to-one relation
  • postProperty: POST /{entity}/{sourceId}/{relation}
    • add items to a *-to-many relation
  • deleteProperty: DELETE /{entity}/{sourceId}/{relation}
    • clear a *-to-one relation
    • clears all items from a *-to-many relation
  • getPropertyItem: GET /{entity}/{sourceId}/{relation}/{targetId}
    • redirect to target entity item of a *-to-many relation
  • deletePropertyItem: DELETE /{entity}/{sourceId}/{relation}/{targetId}
    • remove one item from a *-to-many relation

Add PropertyRestController, it manages all property endpoints (/{entity}/{id}/{property}) and delegates to the correct PropertyRequestHandler:

  • If the PropertyRequestHandler does not find the property, it throws a PropertyNotFoundException to indicate a next PropertyRequestHandler should handle the property. If all PropertyRequestHandler throw PropertyNotFoundException, the response is a 404 Not Found.
  • If the PropertyRequestHandler does not understand the media type, it throws an UnsupportedMediaTypeException to indicate a next PropertyRequestHandler should handle the property and media type. (Allows to support multiple media types for the same property type by creating multiple PropertyRequestHandlers.) If some PropertyRequestHandler throws UnsupportedMediaTypeException and none of the handlers returns a ResponseEntity, it is assumed that the property exists and the response is 415 Unsupported Media Type.
  • If it is able to execute without errors, it simply returns a ResponseEntity<Object>.
  • If it there are other errors while executing the request, it throws an exception with a http error code.

PropertyItemRestController is similar to PropertyRestController, the only difference is that it manages property item endpoints (/{entity}/{entityId}/{property}/{propertyId})

@NielsCW NielsCW force-pushed the ACC-2100 branch 4 times, most recently from 9b0720b to 97815a4 Compare June 6, 2025 09:22
@NielsCW NielsCW marked this pull request as ready for review June 6, 2025 09:23
@NielsCW NielsCW requested a review from a team as a code owner June 6, 2025 09:23
Copy link
Contributor

@thijslemmens thijslemmens left a comment

Choose a reason for hiding this comment

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

As discussed.
Untangle the PropertyRestController from real controllers.
Move the controller mappings to EntityController. Use Factory + Strategy pattern for the different implementations (relation, content, normal property) ...

@NielsCW NielsCW force-pushed the ACC-2100 branch 2 times, most recently from 40267c9 to 1e224da Compare June 17, 2025 08:16
Copy link
Member

@rschev rschev left a comment

Choose a reason for hiding this comment

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

Personally, I think this has more indirection and complexity than we need. If someone new is reading the code, and they find the PropertyItem- or PropertyRestController, it's not clear at all which things handle these endpoints, and in what order. They'd have to start looking through the codebase for things that implement these Handler interfaces, but then that's an abstract class, you have to find what extends this abstract class, and then you still don't know what order these things are tried in.

It might have been a bit more "wordy", but I would have preferred to read, right in the restcontroller, something like:

if (contentPropertyRequestHandler.matches(entityName, property) {
    return contentPropertyRequestHandler.handle(entityName, instanceId, property);
} else if (xToOneRequestHandler.matches(entityName, property) {
    return xToOneRequestHandler.handle(entityName, instanceId, property);
} else if (xToManyRequestHandler.matches(entityName, property) {
    return xToManyRequestHandler.handle(entityName, instanceId, property);
} else {
    // return error
}

datamodelApi.findById(application, property.getSourceEndPoint().getEntity(), instanceId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(instanceId)));
var redirectUrl = linkTo(methodOn(EntityRestController.class).listEntity(application, targetPathSegment, 0,
Map.of(property.getTargetEndPoint().getName().getValue(), instanceId.toString()))).toUri(); // TODO: use RelationSearchFilter
Copy link
Member

Choose a reason for hiding this comment

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

RelationSearchFilter doesn't exist any more :)
We'll leave this for a next PR, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that I see this, I noticed that this throws a NullPointerException when property.getTargetEndPoint().getName() is null

@NielsCW NielsCW force-pushed the ACC-2100 branch 4 times, most recently from 29a1cbb to cfa48cd Compare June 25, 2025 13:19
mvc.problemdetails.enabled: true
server:
port: ${PORT:8080}
max-http-request-header-size: 10MB No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

10MB is beefy, I reckon I can now paste the whole Lord of the Rings trilogy in a single header. What prompted this change?

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 copy pasted this from v1 applications. There it was needed because the gateway adds the residual expression from opa as a thunx expression to the jwt token. And those expressions turned out to be beefy. It is definitely not needed for current state of v2 applications, but might be needed later on.

var targetPathSegment = property.getTargetEndPoint().getEntity().getPathSegment();
datamodelApi.findById(application, property.getSourceEndPoint().getEntity(), instanceId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(instanceId)));
// TODO: use FilterName of relation
Copy link
Member

Choose a reason for hiding this comment

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

I suppose it's not guaranteed that there is a search filter for this relation... Is this appserver's responsibility, or scribe?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's for ticket ACC-2149 to find out how to represent it and how scribe can pass the name of the search filter.

@@ -0,0 +1,5 @@
problemDetail.type.com.contentgrid.appserver.rest.exception.PropertyNotFoundException=https://contentgrid.cloud/problems/not-found/property
problemDetail.title.com.contentgrid.appserver.rest.exception.PropertyNotFoundException=Property not found
Copy link
Member

Choose a reason for hiding this comment

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

Any particular reason why this is the only one to get a title?

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 default title just says "Not found", I guess it is not really necessary to override it as long as the details contains a detailed message which property does not exist.

@thijslemmens thijslemmens dismissed their stale review July 1, 2025 11:28

because the remarks are taken into account

NielsCW added 8 commits July 4, 2025 14:46
Support text/uri-list request body types.
Adds:
- POST /{entityName}/{sourceId}/{relationName} for adding items to *-to-many relations
- PUT /{entityName}/{sourceId}/{relationName} for setting a *-to-one relation
- DELETE /{entityName}/{sourceId}/{relationName} for deleting a relation
- DELETE /{entityName}/{sourceId}/{relationName}/{targetId} for deleting an item from a *-to-many relation
NielsCW added 24 commits July 4, 2025 14:46
…but different mediaType

This is usefull when we want to support file upload with content-type 'multipart/form-data' and '*/*'.
…n and UnsupportedMediaTypeException

Let them extend ResponseStatusException and UnsupportedMediaTypeStatusException from spring web mvc. So that they will automatically be converted to problem details
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jul 4, 2025

@NielsCW NielsCW merged commit 29cb54a into main Jul 4, 2025
3 checks passed
@NielsCW NielsCW deleted the ACC-2100 branch July 4, 2025 13:24
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.

4 participants