Skip to content

Commit 9b0720b

Browse files
committed
ACC-2100 add PropertyRestController + refactor RelationRestController
1 parent b3f2ac3 commit 9b0720b

File tree

4 files changed

+100
-25
lines changed

4 files changed

+100
-25
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.contentgrid.appserver.rest;
2+
3+
import com.contentgrid.appserver.application.model.Application;
4+
import com.contentgrid.appserver.application.model.values.PathSegmentName;
5+
import com.contentgrid.appserver.query.engine.api.data.EntityId;
6+
import java.util.List;
7+
import java.util.Optional;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.http.ResponseEntity;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.bind.annotation.PathVariable;
14+
import org.springframework.web.bind.annotation.RestController;
15+
import org.springframework.web.server.ResponseStatusException;
16+
17+
@RestController
18+
@RequiredArgsConstructor(onConstructor_ = @Autowired)
19+
public class DefaultPropertyRestController {
20+
21+
private final List<PropertyRestController> controllers;
22+
23+
@GetMapping("/{entityName}/{instanceId}/{propertyName}")
24+
public ResponseEntity<Object> getProperty(
25+
Application application,
26+
@PathVariable PathSegmentName entityName,
27+
@PathVariable EntityId instanceId,
28+
@PathVariable PathSegmentName propertyName
29+
) {
30+
var entity = application.getEntityByPathSegment(entityName)
31+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity %s does not exist".formatted(entityName)));
32+
33+
return controllers.stream()
34+
.map(controller -> controller.getProperty(application, entity, instanceId, propertyName))
35+
.flatMap(Optional::stream)
36+
.findFirst()
37+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
38+
"Property %s does not exist on entity %s".formatted(propertyName, entityName)));
39+
}
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.contentgrid.appserver.rest;
2+
3+
import com.contentgrid.appserver.application.model.Application;
4+
import com.contentgrid.appserver.application.model.Entity;
5+
import com.contentgrid.appserver.application.model.values.PathSegmentName;
6+
import com.contentgrid.appserver.query.engine.api.data.EntityId;
7+
import java.util.Optional;
8+
import org.springframework.http.ResponseEntity;
9+
10+
public interface PropertyRestController {
11+
12+
/**
13+
* Access the property. Returns an {@link Optional} containing the result if the property exists, empty otherwise.
14+
* Might throw if the property exists, but accessing the property fails for a different reason.
15+
*
16+
* @param application the application context
17+
* @param entity the entity
18+
* @param instanceId the primary key value of the entity
19+
* @param propertyName the path segment of the property
20+
* @return an {@link Optional} containing the result if property exists, empty otherwise
21+
*/
22+
Optional<ResponseEntity<Object>> getProperty(
23+
Application application,
24+
Entity entity,
25+
EntityId instanceId,
26+
PathSegmentName propertyName
27+
);
28+
29+
}

contentgrid-appserver-rest/src/main/java/com/contentgrid/appserver/rest/RelationRestController.java

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
55

66
import com.contentgrid.appserver.application.model.Application;
7+
import com.contentgrid.appserver.application.model.Entity;
78
import com.contentgrid.appserver.application.model.relations.ManyToManyRelation;
89
import com.contentgrid.appserver.application.model.relations.ManyToOneRelation;
910
import com.contentgrid.appserver.application.model.relations.OneToManyRelation;
@@ -19,13 +20,13 @@
1920
import java.net.URI;
2021
import java.util.List;
2122
import java.util.Map;
23+
import java.util.Optional;
2224
import java.util.UUID;
2325
import lombok.RequiredArgsConstructor;
2426
import org.springframework.beans.factory.annotation.Autowired;
2527
import org.springframework.http.HttpStatus;
2628
import org.springframework.http.ResponseEntity;
2729
import org.springframework.web.bind.annotation.DeleteMapping;
28-
import org.springframework.web.bind.annotation.GetMapping;
2930
import org.springframework.web.bind.annotation.PathVariable;
3031
import org.springframework.web.bind.annotation.PostMapping;
3132
import org.springframework.web.bind.annotation.PutMapping;
@@ -35,7 +36,7 @@
3536

3637
@RestController
3738
@RequiredArgsConstructor(onConstructor_ = @Autowired)
38-
public class RelationRestController {
39+
public class RelationRestController implements PropertyRestController {
3940

4041
private final DatamodelApi datamodelApi;
4142

@@ -44,42 +45,46 @@ private Relation getRelationOrThrow(Application application, PathSegmentName ent
4445
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found"));
4546
}
4647

47-
@GetMapping("/{entityName}/{sourceId}/{relationName}")
48-
public ResponseEntity<Void> followRelation(
48+
@Override
49+
public Optional<ResponseEntity<Object>> getProperty(
4950
Application application,
50-
@PathVariable PathSegmentName entityName,
51-
@PathVariable EntityId sourceId,
52-
@PathVariable PathSegmentName relationName
51+
Entity entity,
52+
EntityId instanceId,
53+
PathSegmentName propertyName
5354
) {
54-
var relation = getRelationOrThrow(application, entityName, relationName);
55+
var maybeRelation = application.getRelationForPath(entity.getPathSegment(), propertyName);
56+
if (maybeRelation.isEmpty()) {
57+
return Optional.empty();
58+
}
59+
var relation = maybeRelation.get();
5560
var targetPathSegment = relation.getTargetEndPoint().getEntity().getPathSegment();
5661
try {
5762
URI redirectUrl;
5863
switch (relation) {
5964
case OneToOneRelation oneToOneRelation -> {
60-
var targetId = datamodelApi.findRelationTarget(application, oneToOneRelation, sourceId)
61-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Target of %s not found".formatted(relationName)));
65+
var targetId = datamodelApi.findRelationTarget(application, oneToOneRelation, instanceId)
66+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Target of %s not found".formatted(propertyName)));
6267
redirectUrl = linkTo(methodOn(EntityRestController.class).getEntity(application, targetPathSegment, targetId)).toUri();
6368
}
6469
case ManyToOneRelation manyToOneRelation -> {
65-
var targetId = datamodelApi.findRelationTarget(application, manyToOneRelation, sourceId)
66-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Target of %s not found".formatted(relationName)));
70+
var targetId = datamodelApi.findRelationTarget(application, manyToOneRelation, instanceId)
71+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Target of %s not found".formatted(propertyName)));
6772
redirectUrl = linkTo(methodOn(EntityRestController.class).getEntity(application, targetPathSegment, targetId)).toUri();
6873
}
6974
case OneToManyRelation oneToManyRelation -> {
70-
datamodelApi.findById(application, oneToManyRelation.getSourceEndPoint().getEntity(), sourceId)
71-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(sourceId)));
75+
datamodelApi.findById(application, oneToManyRelation.getSourceEndPoint().getEntity(), instanceId)
76+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(instanceId)));
7277
redirectUrl = linkTo(methodOn(EntityRestController.class).listEntity(application, targetPathSegment, 0,
73-
Map.of(relation.getTargetEndPoint().getName().getValue(), sourceId.toString()))).toUri(); // TODO: use RelationSearchFilter
78+
Map.of(relation.getTargetEndPoint().getName().getValue(), instanceId.toString()))).toUri(); // TODO: use RelationSearchFilter
7479
}
7580
case ManyToManyRelation manyToManyRelation -> {
76-
datamodelApi.findById(application, manyToManyRelation.getSourceEndPoint().getEntity(), sourceId)
77-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(sourceId)));
81+
datamodelApi.findById(application, manyToManyRelation.getSourceEndPoint().getEntity(), instanceId)
82+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity with id %s not found".formatted(instanceId)));
7883
redirectUrl = linkTo(methodOn(EntityRestController.class).listEntity(application, targetPathSegment, 0,
79-
Map.of(relation.getTargetEndPoint().getName().getValue(), sourceId.toString()))).toUri(); // TODO: use RelationSearchFilter
84+
Map.of(relation.getTargetEndPoint().getName().getValue(), instanceId.toString()))).toUri(); // TODO: use RelationSearchFilter
8085
}
8186
}
82-
return ResponseEntity.status(HttpStatus.FOUND).location(redirectUrl).build();
87+
return Optional.of(ResponseEntity.status(HttpStatus.FOUND).location(redirectUrl).build());
8388
} catch (EntityNotFoundException e) {
8489
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage(), e);
8590
}

contentgrid-appserver-rest/src/main/java/com/contentgrid/appserver/rest/assembler/EntityDataRepresentationModelAssembler.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import com.contentgrid.appserver.application.model.relations.Relation;
1010
import com.contentgrid.appserver.query.engine.api.data.EntityData;
1111
import com.contentgrid.appserver.query.engine.api.data.EntityId;
12+
import com.contentgrid.appserver.rest.DefaultPropertyRestController;
1213
import com.contentgrid.appserver.rest.EntityRestController;
13-
import com.contentgrid.appserver.rest.RelationRestController;
1414
import com.contentgrid.appserver.rest.links.ContentGridLinkRelations;
1515
import com.contentgrid.hateoas.spring.server.RepresentationModelContextAssembler;
1616
import org.springframework.hateoas.Link;
@@ -43,16 +43,17 @@ private Link getSelfLink(Application application, Entity entity, EntityId id) {
4343
}
4444

4545
private Link getRelationLink(Application application, Relation relation, EntityId id) {
46-
return linkTo(methodOn(RelationRestController.class)
47-
.followRelation(application, relation.getSourceEndPoint().getEntity().getPathSegment(), id, relation.getSourceEndPoint().getPathSegment()))
46+
return linkTo(methodOn(DefaultPropertyRestController.class)
47+
.getProperty(application, relation.getSourceEndPoint().getEntity().getPathSegment(), id,
48+
relation.getSourceEndPoint().getPathSegment()))
4849
.withRel(ContentGridLinkRelations.RELATION)
4950
.withName(relation.getSourceEndPoint().getLinkName().getValue());
5051
}
5152

5253
private Link getContentLink(Application application, Entity entity, EntityId id, ContentAttribute attribute) {
53-
// TODO: ACC-2097: use linkTo(methodOn(...)).withRel(CONTENT).withName(attribute.getLinkName().getValue())
54-
var href = getSelfLink(application, entity, id).getHref() + "/" + attribute.getPathSegment();
55-
return Link.of(href, ContentGridLinkRelations.CONTENT)
54+
return linkTo(methodOn(DefaultPropertyRestController.class)
55+
.getProperty(application, entity.getPathSegment(), id, attribute.getPathSegment()))
56+
.withRel(ContentGridLinkRelations.CONTENT)
5657
.withName(attribute.getLinkName().getValue());
5758
}
5859
}

0 commit comments

Comments
 (0)