Skip to content

Commit

Permalink
Feat: constraint component (#145)
Browse files Browse the repository at this point in the history
* Created the constraint signature component

* Rebase

* Added shadcn components I forgot to push

* chore: refactoring

* Rebase

* rebase

* Unfinished table

* fixed for new constraints endpoint

* Checkstyle

* data table for constraint instances

* Fixed styling of page and components

* constraint add button

* fixed delete, removed add button and sorting from table

* Added yet another DTO to hopefully make the request parameters easier to understand

* Updated openapi

* Fixed NPE when getting instances of a constraint with 0 instances

* Fixed exception when creating a new constraint instance through POST request

* Fixed add button

* Fixed data table updates

* Fixed web tests

* Set page size for datatable

* Removed unused component

* Changes from comments and pagination fix for datatable

* Fixed imports

* Fixed package-lock

---------

Co-authored-by: uhkfc <daniel.dietzler@student.kit.edu>
Co-authored-by: Lars Steudle <uojhg@student.kit.edu>
  • Loading branch information
3 people authored Jul 28, 2024
1 parent 6daae6d commit 642eb8f
Show file tree
Hide file tree
Showing 38 changed files with 977 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public class ConstraintController {
private final ConstraintSignatureService constraintSignatureService;
private final ConstraintInstanceService constraintInstanceService;


/**
* Creates the constraint controller.
*
Expand All @@ -41,7 +40,6 @@ public ConstraintController(ConstraintSignatureService constraintSignatureServic
ConstraintInstanceService constraintInstanceService) {
this.constraintSignatureService = constraintSignatureService;
this.constraintInstanceService = constraintInstanceService;

}

/**
Expand All @@ -56,6 +54,7 @@ public ConstraintController(ConstraintSignatureService constraintSignatureServic
@GetMapping()
public Page<ConstraintSignature> getConstraintSignatures(Pageable pageable,
Optional<String> name) {

return this.constraintSignatureService.get(pageable, name);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package de.uftos.dto;

import de.uftos.entities.ConstraintArgument;
import jakarta.validation.constraints.NotEmpty;

/**
* A data transfer object used in the constraint HTTP requests.
*
* @param parameterName the name of the parameter that this argument instances.
* @param argumentId the id ot the entity that this argument references.
*/
public record ConstraintArgumentRequestDto(@NotEmpty String parameterName,
@NotEmpty String argumentId) {
/**
* Maps the information from the data transfer object to a new constraint argument entity.
*
* @return the new constraint argument entity.
*/
public ConstraintArgument map() {
return new ConstraintArgument(parameterName, argumentId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import de.uftos.dto.solver.RewardPenalize;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
import java.util.List;

/**
* A data transfer object used in the constraint HTTP requests.
*
* @param arguments a mapping of parameter name to value.
* @param type the type of the specific constraint instance.
*/
public record ConstraintInstanceRequestDto(@NotNull Map<String, String> arguments,
public record ConstraintInstanceRequestDto(@NotNull List<ConstraintArgumentRequestDto> arguments,
RewardPenalize type) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import de.uftos.entities.ConstraintArgument;
import de.uftos.entities.ConstraintInstance;
import de.uftos.entities.ConstraintParameter;
import de.uftos.entities.ConstraintSignature;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.stream.Collectors;

/**
* A data transfer object used in the curriculum HTTP requests.
Expand All @@ -25,12 +27,16 @@ public record ConstraintInstancesResponseDto(@NotNull List<SlimInstance> constra
*
* @param constraintInstances the instances of the constraint
* @param displayNames the display names for the arguments
* @param constraintSignature the signature from which to get the parameters
*/
public ConstraintInstancesResponseDto(List<ConstraintInstance> constraintInstances,
List<ConstraintArgumentDisplayName> displayNames) {
this(constraintInstances.stream().map(SlimInstance::new).toList(),
List<ConstraintArgumentDisplayName> displayNames,
ConstraintSignature constraintSignature) {
this(
constraintInstances.stream().map(SlimInstance::new).collect(Collectors.toList()),
displayNames,
constraintInstances.getFirst().getSignature().getParameters());
constraintSignature.getParameters()
);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions server/src/main/java/de/uftos/dto/solver/RewardPenalize.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package de.uftos.dto.solver;

import io.swagger.v3.oas.annotations.media.Schema;

/**
* Describes whether a constraint instance is soft or hard
* and whether the constraint gets rewarded or penalized.
*/
@Schema(enumAsRef = true)
public enum RewardPenalize {
SOFT_REWARD,
HARD_REWARD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ public class ConstraintArgument {
@NotEmpty
private String value;


/**
* Creates a new constraint argument.
*
* @param name the of the parameter this argument refers to
* @param value the id of the entity this argument refers to
*/
public ConstraintArgument(String name, String value) {
this.constraintParameter = new ConstraintParameter(name);
this.value = value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
import java.util.List;
import java.util.Objects;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* The database table for constraint parameters.
* Contains an ID, the name and type of the parameter as well as the constraint signature using it.
*/
@Entity(name = "constraint_parameter")
@Data
@NoArgsConstructor
public class ConstraintParameter {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
Expand All @@ -37,6 +39,10 @@ public class ConstraintParameter {
@Enumerated(EnumType.STRING)
private ResourceType parameterType;

public ConstraintParameter(String parameterName) {
this.parameterName = parameterName;
}

@Override
public boolean equals(Object other) {
if (this == other) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.uftos.services;

import de.uftos.dto.ConstraintArgumentDisplayName;
import de.uftos.dto.ConstraintArgumentRequestDto;
import de.uftos.dto.ConstraintInstanceRequestDto;
import de.uftos.dto.ConstraintInstancesResponseDto;
import de.uftos.dto.ResourceType;
Expand Down Expand Up @@ -99,24 +100,29 @@ public ConstraintInstance create(String signatureId, ConstraintInstanceRequestDt
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}

List<ConstraintArgument> arguments = new ArrayList<>();
for (ConstraintParameter parameter : signature.getParameters()) {
String value = request.arguments().get(parameter.getParameterName());
if (value == null || value.isEmpty()) {
Optional<ConstraintArgumentRequestDto> argument = request.arguments().stream()
.filter(arg -> arg.parameterName().equals(parameter.getParameterName()))
.findFirst();
if (argument.isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Parameter %s could not be found".formatted(parameter.getParameterName()));
}

boolean exists = this.getResourceTypeMapping(value).get(parameter.getParameterType());
boolean exists = this.getResourceTypeMapping(argument.get().argumentId())
.get(parameter.getParameterType());
if (!exists) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"%s with id %s could not be found".formatted(parameter.getParameterName(),
parameter.getId()));
}
ConstraintArgument arg = argument.get().map();
arg.setConstraintParameter(parameter);
arguments.add(arg);
}

ConstraintInstance instance = new ConstraintInstance();
List<ConstraintArgument> arguments = request.arguments().entrySet().stream()
.map(item -> new ConstraintArgument(item.getKey(), item.getValue())).toList();
instance.setArguments(arguments);
instance.setSignature(signature);
instance.setType(request.type());
Expand All @@ -141,9 +147,14 @@ public ConstraintInstancesResponseDto get(String signatureId, Pageable pageable,
.build();

Page<ConstraintInstance> constraintInstances = this.repository.findAll(specification, pageable);
Optional<ConstraintSignature> signature = this.signatureRepository.findById(signatureId);
List<ConstraintArgumentDisplayName> displayNames =
processConstraintInstances(constraintInstances.getContent());
return new ConstraintInstancesResponseDto(constraintInstances.getContent(), displayNames);
return new ConstraintInstancesResponseDto(
constraintInstances.getContent(),
displayNames,
signature.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST))
);
}

/**
Expand All @@ -157,7 +168,12 @@ public ConstraintInstancesResponseDto getById(String signatureId, String id) {
ConstraintInstance constraintInstance = getInstanceById(signatureId, id);
List<ConstraintArgumentDisplayName> displayNames =
getDisplayNamesFromInstances(constraintInstance);
return new ConstraintInstancesResponseDto(List.of(constraintInstance), displayNames);
Optional<ConstraintSignature> signature = this.signatureRepository.findById(signatureId);
return new ConstraintInstancesResponseDto(
List.of(constraintInstance),
displayNames,
signature.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST))
);
}

/**
Expand All @@ -171,8 +187,9 @@ public ConstraintInstance update(String signatureId, String id,
ConstraintInstanceRequestDto request) {
ConstraintInstance instance = this.getInstanceById(signatureId, id);

List<ConstraintArgument> arguments = request.arguments().entrySet().stream()
.map(item -> new ConstraintArgument(item.getKey(), item.getValue())).toList();
List<ConstraintArgument> arguments = request.arguments().stream()
.map(ConstraintArgumentRequestDto::map)
.toList();

instance.setArguments(arguments);
instance.setType(request.type());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

import de.uftos.dto.ConstraintArgumentRequestDto;
import de.uftos.dto.ConstraintInstanceRequestDto;
import de.uftos.dto.ConstraintInstancesResponseDto;
import de.uftos.dto.ResourceType;
Expand All @@ -28,7 +29,6 @@
import de.uftos.repositories.database.TeacherRepository;
import de.uftos.repositories.database.TimeslotRepository;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -139,33 +139,34 @@ void constrainInstanceById() {

@Test
void nonExistentParameter() {
ConstraintInstanceRequestDto constraintInstanceRequestDto = new ConstraintInstanceRequestDto(
Map.of(
"teacher123", "teacherId2"
), RewardPenalize.HARD_PENALIZE);
ConstraintArgumentRequestDto arg =
new ConstraintArgumentRequestDto("teacher123", "teacherId2");
ConstraintInstanceRequestDto constraintInstanceRequestDto =
new ConstraintInstanceRequestDto(List.of(arg), RewardPenalize.HARD_PENALIZE);

assertThrows(ResponseStatusException.class,
() -> constraintInstanceService.create("test constraint", constraintInstanceRequestDto));
}

@Test
void tooManyParameter() {
ConstraintInstanceRequestDto constraintInstanceRequestDto = new ConstraintInstanceRequestDto(
Map.of(
"teacher123", "teacherId1",
"teacher456", "teacherId1"
), RewardPenalize.HARD_PENALIZE);
ConstraintArgumentRequestDto arg1 =
new ConstraintArgumentRequestDto("teacher123", "teacherId1");
ConstraintArgumentRequestDto arg2 =
new ConstraintArgumentRequestDto("teacher456", "teacherId1");
ConstraintInstanceRequestDto constraintInstanceRequestDto =
new ConstraintInstanceRequestDto(List.of(arg1, arg2), RewardPenalize.HARD_PENALIZE);

assertThrows(ResponseStatusException.class,
() -> constraintInstanceService.create("test constraint", constraintInstanceRequestDto));
}

@Test
void createConstraintInstance() {
ConstraintInstanceRequestDto constraintInstanceRequestDto = new ConstraintInstanceRequestDto(
Map.of(
"teacher123", "teacherId1"
), RewardPenalize.HARD_PENALIZE);
ConstraintArgumentRequestDto arg =
new ConstraintArgumentRequestDto("teacher123", "teacherId1");
ConstraintInstanceRequestDto constraintInstanceRequestDto =
new ConstraintInstanceRequestDto(List.of(arg), RewardPenalize.HARD_PENALIZE);

assertDoesNotThrow(
() -> constraintInstanceService.create("test constraint", constraintInstanceRequestDto));
Expand Down
34 changes: 23 additions & 11 deletions uftos-openapi-specs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2179,6 +2179,18 @@
}
}
},
"ConstraintArgumentRequestDto" : {
"required" : [ "argumentId", "parameterName" ],
"type" : "object",
"properties" : {
"argumentId" : {
"type" : "string"
},
"parameterName" : {
"type" : "string"
}
}
},
"ConstraintInstance" : {
"required" : [ "arguments", "id", "type" ],
"type" : "object",
Expand All @@ -2193,8 +2205,7 @@
"type" : "string"
},
"type" : {
"type" : "string",
"enum" : [ "SOFT_REWARD", "HARD_REWARD", "SOFT_PENALIZE", "HARD_PENALIZE" ]
"$ref" : "#/components/schemas/RewardPenalize"
}
}
},
Expand All @@ -2203,14 +2214,13 @@
"type" : "object",
"properties" : {
"arguments" : {
"type" : "object",
"additionalProperties" : {
"type" : "string"
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/ConstraintArgumentRequestDto"
}
},
"type" : {
"type" : "string",
"enum" : [ "SOFT_REWARD", "HARD_REWARD", "SOFT_PENALIZE", "HARD_PENALIZE" ]
"$ref" : "#/components/schemas/RewardPenalize"
}
}
},
Expand Down Expand Up @@ -2259,8 +2269,7 @@
"type" : "object",
"properties" : {
"defaultType" : {
"type" : "string",
"enum" : [ "SOFT_REWARD", "HARD_REWARD", "SOFT_PENALIZE", "HARD_PENALIZE" ]
"$ref" : "#/components/schemas/RewardPenalize"
},
"description" : {
"type" : "string"
Expand Down Expand Up @@ -2999,6 +3008,10 @@
}
}
},
"RewardPenalize" : {
"type" : "string",
"enum" : [ "SOFT_REWARD", "HARD_REWARD", "SOFT_PENALIZE", "HARD_PENALIZE" ]
},
"Room" : {
"required" : [ "buildingName", "capacity", "id", "name", "tags" ],
"type" : "object",
Expand Down Expand Up @@ -3098,8 +3111,7 @@
"type" : "string"
},
"type" : {
"type" : "string",
"enum" : [ "SOFT_REWARD", "HARD_REWARD", "SOFT_PENALIZE", "HARD_PENALIZE" ]
"$ref" : "#/components/schemas/RewardPenalize"
}
}
},
Expand Down
Loading

0 comments on commit 642eb8f

Please sign in to comment.