добавил реализацию и тесты property
This commit is contained in:
parent
46e48f0984
commit
3d6b3d8059
|
|
@ -0,0 +1,29 @@
|
|||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
import ru.kirillius.XCP.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.Properties.Constraints;
|
||||
import ru.kirillius.XCP.Properties.PropertyType;
|
||||
|
||||
@GenerateApiSpec
|
||||
public interface PropertyDescriptor extends PersistenceEntity {
|
||||
@GenerateApiSpec
|
||||
Constraints getConstraints();
|
||||
|
||||
void setConstraints(Constraints constraints);
|
||||
|
||||
@GenerateApiSpec
|
||||
boolean isArray();
|
||||
|
||||
void setArray(boolean isArray);
|
||||
|
||||
@GenerateApiSpec
|
||||
PropertyType getPropertyType();
|
||||
|
||||
void setPropertyType(PropertyType propertyType);
|
||||
|
||||
@GenerateApiSpec
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.Entities.PropertyDescriptor;
|
||||
import ru.kirillius.XCP.Persistence.Repository;
|
||||
|
||||
public interface PropertyDescriptorRepository extends Repository<PropertyDescriptor> {
|
||||
PropertyDescriptor getByName(String name);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
|
||||
@GenerateApiSpec(directInheritors = {
|
||||
NumberConstraint.class,
|
||||
ValueListConstraint.class,
|
||||
StringConstraint.class
|
||||
})
|
||||
public interface Constraint {
|
||||
@JsonProperty(value = "type")
|
||||
default String type() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface Constraints extends Set<Constraint> {
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@GenerateApiSpec
|
||||
@NoArgsConstructor
|
||||
public final class NumberConstraint implements Constraint {
|
||||
@JsonProperty
|
||||
private double min;
|
||||
@JsonProperty
|
||||
private double max;
|
||||
@JsonProperty
|
||||
private double step;
|
||||
@JsonProperty
|
||||
private boolean integer;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
public enum PropertyType {
|
||||
Number,
|
||||
Boolean,
|
||||
Text,
|
||||
ValueList,
|
||||
JSON
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@GenerateApiSpec
|
||||
@NoArgsConstructor
|
||||
public final class StringConstraint implements Constraint {
|
||||
@JsonProperty
|
||||
private int maxLength;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package ru.kirillius.XCP.Properties;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@GenerateApiSpec
|
||||
@NoArgsConstructor
|
||||
public final class ValueListConstraint implements Constraint {
|
||||
@JsonProperty
|
||||
private Collection<String> values;
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import ru.kirillius.XCP.Commons.GenerateApiSpec;
|
||||
import ru.kirillius.XCP.Persistence.AbstractEntity;
|
||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
||||
import ru.kirillius.XCP.Persistence.Entities.PropertyDescriptor;
|
||||
import ru.kirillius.XCP.Persistence.EntityImplementation;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
||||
import ru.kirillius.XCP.Properties.Constraint;
|
||||
import ru.kirillius.XCP.Properties.Constraints;
|
||||
import ru.kirillius.XCP.Properties.PropertyType;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.node.ArrayNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
@EntityImplementation(PropertyDescriptorRepositoryImpl.PropertyDescriptorEntity.class)
|
||||
public class PropertyDescriptorRepositoryImpl extends AbstractRepository<PropertyDescriptor> implements PropertyDescriptorRepository {
|
||||
|
||||
public PropertyDescriptorRepositoryImpl(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyDescriptor getByName(String name) {
|
||||
try (var request = buildQueryParametrized("WHERE name = ?1", name)) {
|
||||
return request.get().findFirst().orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "PropertyDescriptors")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class PropertyDescriptorEntity extends AbstractEntity implements PropertyDescriptor {
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
@JsonProperty
|
||||
private String name = "";
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonIgnore
|
||||
@Column(nullable = false, name = "constraints_set")
|
||||
@Convert(converter = ConstraintsConverter.class)
|
||||
private Constraints constraints = new ConstraintsImpl();
|
||||
|
||||
@Transient
|
||||
@JsonProperty("constraints")
|
||||
ArrayNode getConstraintsSerialized() {
|
||||
return ConstraintsImpl.serialize(constraints);
|
||||
}
|
||||
|
||||
@Transient
|
||||
@JsonProperty("constraints")
|
||||
void setConstraintsSerialized(ArrayNode array) {
|
||||
constraints = ConstraintsImpl.deserialize(array);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonProperty
|
||||
@Column(name = "is_array")
|
||||
private boolean array;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonProperty
|
||||
@Column(nullable = false)
|
||||
private PropertyType propertyType = PropertyType.Text;
|
||||
}
|
||||
|
||||
private final static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private static final class ConstraintsImpl extends HashSet<Constraint> implements Constraints {
|
||||
|
||||
//TODO перенести в отдельный класс сериализации
|
||||
static ArrayNode serialize(Constraints constraints) {
|
||||
var array = mapper.createArrayNode();
|
||||
constraints.forEach(c -> array.add(mapper.valueToTree(c)));
|
||||
return array;
|
||||
}
|
||||
|
||||
static Constraints deserialize(ArrayNode array) {
|
||||
var inheritors = Constraint.class.getAnnotation(GenerateApiSpec.class).directInheritors();
|
||||
var constraints = new ConstraintsImpl();
|
||||
array.forEach(node -> {
|
||||
var typeName = node.get("type").asString();
|
||||
var type = Arrays.stream(inheritors).filter(cls -> cls.getSimpleName().equalsIgnoreCase(typeName)).findFirst().orElseThrow(() -> new RuntimeException("Invalid type: " + typeName));
|
||||
constraints.add((Constraint) mapper.convertValue(node, type));
|
||||
});
|
||||
return constraints;
|
||||
}
|
||||
}
|
||||
|
||||
public final static class ConstraintsConverter implements AttributeConverter<Constraints, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Constraints constraints) {
|
||||
return mapper.writeValueAsString(ConstraintsImpl.serialize(constraints));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constraints convertToEntityAttribute(String s) {
|
||||
return ConstraintsImpl.deserialize((ArrayNode) mapper.readTree(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.Persistence.Entities.PropertyDescriptor;
|
||||
import ru.kirillius.XCP.Persistence.PersistenceException;
|
||||
import ru.kirillius.XCP.Properties.NumberConstraint;
|
||||
import ru.kirillius.XCP.Properties.StringConstraint;
|
||||
import ru.kirillius.XCP.Services.RepositoryService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PropertyDescriptorRepositoryImplTest extends GenericRepositoryTest<PropertyDescriptor, PropertyDescriptorRepositoryImpl> {
|
||||
|
||||
@Test
|
||||
void saveSameName() throws IOException {
|
||||
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(PropertyDescriptorRepository.class);
|
||||
|
||||
var propertyDescriptor = repository.create();
|
||||
propertyDescriptor.setName("test");
|
||||
repository.save(propertyDescriptor);
|
||||
|
||||
propertyDescriptor = repository.create();
|
||||
propertyDescriptor.setName("test");
|
||||
try {
|
||||
repository.save(propertyDescriptor);
|
||||
throw new AssertionError("Should have thrown an exception");
|
||||
} catch (Throwable t) {
|
||||
assertThat(t).isInstanceOf(PersistenceException.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getByName() throws IOException {
|
||||
var correct = "correctName";
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(PropertyDescriptorRepository.class);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var propertyDescriptor = repository.create();
|
||||
propertyDescriptor.setName("incorrect" + UUID.randomUUID());
|
||||
repository.save(propertyDescriptor);
|
||||
}
|
||||
var correctDescriptor = repository.create();
|
||||
correctDescriptor.setName(correct);
|
||||
repository.save(correctDescriptor);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var propertyDescriptor = repository.create();
|
||||
propertyDescriptor.setName("incorrect" + UUID.randomUUID());
|
||||
repository.save(propertyDescriptor);
|
||||
}
|
||||
|
||||
var loaded = repository.getByName(correct);
|
||||
assertThat(loaded).isNotNull().isEqualTo(correctDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(PropertyDescriptor entity, RepositoryService service) {
|
||||
entity.setName("test" + UUID.randomUUID());
|
||||
entity.getConstraints().add(NumberConstraint.builder().build());
|
||||
entity.getConstraints().add(StringConstraint.builder().build());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package ru.kirillius.XCP.RPC.Services;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.Entities.PropertyDescriptor;
|
||||
import ru.kirillius.XCP.Persistence.Repositories.PropertyDescriptorRepository;
|
||||
import ru.kirillius.XCP.RPC.JSONRPC.CallContext;
|
||||
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcMethod;
|
||||
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcService;
|
||||
import ru.kirillius.XCP.Security.UserRole;
|
||||
import ru.kirillius.XCP.Services.RepositoryService;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.node.ArrayNode;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Properties extends JsonRpcService {
|
||||
@JsonRpcMethod(
|
||||
accessLevel = UserRole.User,
|
||||
description = "Get all properties as list",
|
||||
parameters = {},
|
||||
returnType = PropertyDescriptor[].class
|
||||
)
|
||||
public ArrayNode getAll(CallContext call) throws IOException {
|
||||
var repository = call.getContext().getService(RepositoryService.class).getRepository(PropertyDescriptorRepository.class);
|
||||
try (var handler = repository.getAll()) {
|
||||
return repository.serialize(handler.get().toList());
|
||||
}
|
||||
}
|
||||
|
||||
@JsonRpcMethod(
|
||||
accessLevel = UserRole.User,
|
||||
description = "Load property descriptor object by name",
|
||||
parameters = {
|
||||
@JsonRpcMethod.Parameter(name = "name", description = "property name", type = String.class)
|
||||
},
|
||||
returnType = PropertyDescriptor.class
|
||||
)
|
||||
public ObjectNode getByName(CallContext call) {
|
||||
var name = requireParam(call, "name", JsonNode::asString);
|
||||
var repository = call.getContext().getService(RepositoryService.class).getRepository(PropertyDescriptorRepository.class);
|
||||
return repository.serialize(repository.getByName(name));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue