Compare commits
3 Commits
e29286cce3
...
b264b38fe4
| Author | SHA1 | Date |
|---|---|---|
|
|
b264b38fe4 | |
|
|
f50fd2a474 | |
|
|
8e2412f36a |
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import ru.kirillius.XCP.Security.HashUtility;
|
||||
|
||||
public interface Context {
|
||||
Config getConfig();
|
||||
|
|
@ -8,4 +10,6 @@ public interface Context {
|
|||
<S extends Service> S getService(Class<S> serviceClass);
|
||||
|
||||
void shutdown();
|
||||
|
||||
HashUtility getHashUtility();
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Commons;
|
||||
package ru.kirillius.XCP.Commons;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Data;
|
||||
package ru.kirillius.XCP.Data;
|
||||
|
||||
@Deprecated
|
||||
public interface DataTransferProtocol {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Data;
|
||||
package ru.kirillius.XCP.Data;
|
||||
|
||||
public interface PollSettings {
|
||||
int getMaxValueCount();
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Data;
|
||||
package ru.kirillius.XCP.Data;
|
||||
|
||||
@Deprecated
|
||||
public interface ValueModifier {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Data;
|
||||
package ru.kirillius.XCP.Data;
|
||||
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.NodeEntity;
|
||||
|
||||
public interface Group extends NodeEntity {
|
||||
String getIcon();
|
||||
|
||||
void setIcon(String icon);
|
||||
|
||||
boolean isPrototype();
|
||||
|
||||
void setPrototype(boolean prototype);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.Data.PollSettings;
|
||||
import ru.kirillius.XCP.Persistence.IOEntity;
|
||||
import ru.kirillius.XCP.Persistence.NodeEntity;
|
||||
|
||||
public interface Input extends IOEntity, NodeEntity {
|
||||
|
||||
PollSettings getPollSettings();
|
||||
|
||||
void setPollSettings(PollSettings pollSettings);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.IOEntity;
|
||||
import ru.kirillius.XCP.Persistence.NodeEntity;
|
||||
|
||||
public interface Output extends IOEntity, NodeEntity {
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.PersistenceEntity;
|
||||
|
||||
public interface Tag extends PersistenceEntity {
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Entities;
|
||||
package ru.kirillius.XCP.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.api.Security.UserRole;
|
||||
import ru.kirillius.XCP.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.Security.UserRole;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
public interface User extends PersistenceEntity {
|
||||
void changePassword(String newPass);
|
||||
void setPassword(String password);
|
||||
|
||||
boolean verifyPassword(String password);
|
||||
|
||||
String getLogin();
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Data.DataTransferProtocol;
|
||||
import ru.kirillius.XCP.api.Data.ValueModifierSettings;
|
||||
import ru.kirillius.XCP.Data.DataTransferProtocol;
|
||||
import ru.kirillius.XCP.Data.ValueModifierSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.util.Set;
|
||||
|
|
@ -10,9 +11,9 @@ public interface NodeEntity extends PersistenceEntity {
|
|||
|
||||
void setName(String name);
|
||||
|
||||
boolean isEssential();
|
||||
boolean isProtectedEntity();
|
||||
|
||||
void setEssential(boolean essential);
|
||||
void setProtectedEntity(boolean essential);
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ public interface NodeEntity extends PersistenceEntity {
|
|||
|
||||
void setProperties(ObjectNode properties);
|
||||
|
||||
Set<String> getTags();
|
||||
Set<Tag> getTags();
|
||||
|
||||
void setTags(Set<String> tags);
|
||||
void setTags(Set<Tag> tags);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface NodeRepository<E extends NodeEntity> extends Repository<E> {
|
||||
StreamHandler<E> getByGroup(Group group);
|
||||
|
||||
StreamHandler<E> getByTags(Collection<Tag> tags);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PersistenceEntity {
|
||||
long getId();
|
||||
|
||||
UUID getUuid();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.NodeRepository;
|
||||
|
||||
public interface GroupRepository extends NodeRepository<Group> {
|
||||
StreamHandler<Group> getChildrenOf(Group group);
|
||||
|
||||
StreamHandler<Group> getAllChildrenInHierarchy(Group group);
|
||||
|
||||
Group getRoot();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.Entities.Input;
|
||||
import ru.kirillius.XCP.Persistence.NodeRepository;
|
||||
|
||||
public interface InputRepository extends NodeRepository<Input> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.Entities.Input;
|
||||
import ru.kirillius.XCP.Persistence.NodeRepository;
|
||||
|
||||
public interface OutputRepository extends NodeRepository<Input> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Commons.ResourceHandler;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
import ru.kirillius.XCP.Persistence.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface TagRepository extends Repository<Tag> {
|
||||
Tag getByName(String name);
|
||||
|
||||
ResourceHandler<Stream<Tag>> getByNames(Collection<String> names);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.Persistence.Entities.User;
|
||||
import ru.kirillius.XCP.Persistence.Repository;
|
||||
|
||||
public interface UserRepository extends Repository<User> {
|
||||
|
||||
User getByLogin(String login);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.java.utils.events.EventHandler;
|
||||
import tools.jackson.databind.node.ArrayNode;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.Commons.Service;
|
||||
|
||||
public interface RepositoryService extends Service {
|
||||
<E extends PersistenceEntity> Repository<E> getRepositoryForEntity(Class<E> entityType);
|
||||
|
||||
<R extends Repository<?>> R getRepository(Class<R> repositoryType);
|
||||
|
||||
Class<? extends PersistenceEntity> getEntityBaseType(Class<? extends PersistenceEntity> entityClass);
|
||||
Class<? extends Repository<?>> getRepositoryBaseType(Class<? extends Repository<?>> repositoryClass);
|
||||
Class<? extends PersistenceEntity> getRepositoryEntityType(Class<? extends Repository<?>> repositoryClass);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Security;
|
||||
|
||||
public interface HashUtility {
|
||||
String hash(String password);
|
||||
|
||||
default boolean validate(String password, String hash) {
|
||||
return hash(password).equals(hash);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.kirillius.XCP.api.Security;
|
||||
package ru.kirillius.XCP.Security;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.NodeEntity;
|
||||
|
||||
public interface Group extends NodeEntity {
|
||||
String getIcon();
|
||||
void setIcon(String icon);
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.api.Data.PollSettings;
|
||||
import ru.kirillius.XCP.api.Persistence.IOEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.NodeEntity;
|
||||
|
||||
public interface Input extends IOEntity, NodeEntity {
|
||||
|
||||
PollSettings getPollSettings();
|
||||
|
||||
void setPollSettings(PollSettings pollSettings);
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Entities;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.IOEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.NodeEntity;
|
||||
|
||||
public interface Output extends IOEntity, NodeEntity {
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Group;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface NodeRepository<E extends NodeEntity> extends Repository<E> {
|
||||
StreamHandler<E> getByGroup(Group group);
|
||||
|
||||
StreamHandler<E> getByTags(Collection<String> tags, TagSearchMode searchMode);
|
||||
|
||||
enum TagSearchMode {
|
||||
MatchAnyTag,
|
||||
MatchAllTags,
|
||||
MatchExactTags
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PersistenceEntity {
|
||||
long getId();
|
||||
|
||||
UUID getUUID();
|
||||
|
||||
Class<? extends PersistenceEntity> getBaseType();
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.api.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Input;
|
||||
import ru.kirillius.XCP.api.Persistence.NodeRepository;
|
||||
|
||||
public interface GroupRepository extends NodeRepository<Input> {
|
||||
StreamHandler<Group> getChildrenOf(Group dataGroup);
|
||||
|
||||
StreamHandler<Group> getChildrenRecursiveOf(Group dataGroup);
|
||||
|
||||
Group getRoot();
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Input;
|
||||
import ru.kirillius.XCP.api.Persistence.NodeRepository;
|
||||
|
||||
public interface InputRepository extends NodeRepository<Input> {
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.Input;
|
||||
import ru.kirillius.XCP.api.Persistence.NodeRepository;
|
||||
|
||||
public interface OutputRepository extends NodeRepository<Input> {
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence.Repositories;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.User;
|
||||
import ru.kirillius.XCP.api.Persistence.Repository;
|
||||
|
||||
public interface UserRepository extends Repository<User> {
|
||||
User getByLoginAndPassword(String login, String password);
|
||||
|
||||
User getByLogin(String login);
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package ru.kirillius.XCP.api.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Commons.Service;
|
||||
|
||||
public interface RepositoryService extends Service {
|
||||
<E extends PersistenceEntity> Repository<E> getRepositoryForEntity(Class<E> entityType);
|
||||
|
||||
<R extends Repository<?>> R getRepository(Class<R> repositoryType);
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>ru.kirillius</groupId>
|
||||
<artifactId>XCP</artifactId>
|
||||
<version>1.0.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>core</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ru.kirillius</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package ru.kirillius.XCP.Security;
|
||||
|
||||
import de.mkammerer.argon2.Argon2;
|
||||
import de.mkammerer.argon2.Argon2Factory;
|
||||
import lombok.Getter;
|
||||
|
||||
public class Argon2HashUtility implements HashUtility {
|
||||
|
||||
@Getter
|
||||
private final static HashUtility instance = new Argon2HashUtility();
|
||||
|
||||
private final Argon2 argon2;
|
||||
|
||||
public Argon2HashUtility() {
|
||||
this.argon2 = Argon2Factory.create(
|
||||
Argon2Factory.Argon2Types.ARGON2id,
|
||||
16,
|
||||
32
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String hash(String password) {
|
||||
try {
|
||||
return argon2.hash(3, 65536, 1, password.toCharArray());
|
||||
} finally {
|
||||
argon2.wipeArray(password.toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(String password, String hash) {
|
||||
return argon2.verify(hash, password.toCharArray());
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,12 @@
|
|||
<version>1.0.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.kirillius</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>1.0.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.query.Query;
|
||||
import ru.kirillius.XCP.api.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.Repository;
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.java.utils.events.ConcurrentEventHandler;
|
||||
import ru.kirillius.java.utils.events.EventHandler;
|
||||
import tools.jackson.databind.node.ArrayNode;
|
||||
|
|
@ -15,7 +11,6 @@ import tools.jackson.databind.node.ObjectNode;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
|
@ -23,38 +18,19 @@ import java.util.UUID;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractRepository<E extends PersistenceEntity> implements Repository<E> {
|
||||
protected final Class<? extends E> entityImplementationClass;
|
||||
|
||||
private final Repository.EventBindings<E> eventBindings = new EventBindingsImpl<>();
|
||||
@Getter
|
||||
protected Class<? extends E> entityClass;
|
||||
protected final Class<? extends E> entityImplementationClass;
|
||||
protected RepositoryServiceImpl repositoryService;
|
||||
protected String tableName;
|
||||
|
||||
public AbstractRepository(RepositoryServiceImpl repositoryService) {
|
||||
var thisClass = getClass();
|
||||
|
||||
if (!thisClass.isAnnotationPresent(EntityImplementation.class)) {
|
||||
throw new IllegalStateException("Unable to find @" + EntityImplementation.class.getSimpleName() + " in class " + thisClass.getSimpleName());
|
||||
}
|
||||
|
||||
this.repositoryService = repositoryService;
|
||||
|
||||
entityClass = getGenericParameterType();
|
||||
var implClass = thisClass.getAnnotation(EntityImplementation.class).value();
|
||||
|
||||
if (!entityClass.isAssignableFrom(implClass)) {
|
||||
throw new IllegalStateException("Class " + implClass.getSimpleName() + " should implement " + entityClass.getSimpleName());
|
||||
}
|
||||
//noinspection unchecked
|
||||
entityImplementationClass = (Class<? extends E>) thisClass.getAnnotation(EntityImplementation.class).value();
|
||||
|
||||
if (entityImplementationClass.isAnnotationPresent(Table.class)) {
|
||||
tableName = entityImplementationClass.getAnnotation(Table.class).name();
|
||||
}
|
||||
if (tableName == null || tableName.isEmpty()) {
|
||||
tableName = entityImplementationClass.getName();
|
||||
}
|
||||
|
||||
tableName = entityImplementationClass.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -68,8 +44,6 @@ public abstract class AbstractRepository<E extends PersistenceEntity> implements
|
|||
}
|
||||
}
|
||||
|
||||
public abstract Class<? extends Repository<E>> getBaseClass();
|
||||
|
||||
@Override
|
||||
public StreamHandler<E> search(String query, Collection<Object> queryParameters) {
|
||||
return buildQuery(query, queryParameters.toArray());
|
||||
|
|
@ -205,21 +179,6 @@ public abstract class AbstractRepository<E extends PersistenceEntity> implements
|
|||
return repositoryService.getMapper().treeToValue(array, repositoryService.getMapper().getTypeFactory().constructCollectionType(List.class, entityImplementationClass));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<E> getGenericParameterType() {
|
||||
try {
|
||||
var parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
|
||||
var typeArguments = parameterizedType.getActualTypeArguments();
|
||||
|
||||
if (typeArguments.length != 1) {
|
||||
throw new IllegalStateException("Generic parameters count is unsupported");
|
||||
}
|
||||
return (Class<E>) typeArguments[0];
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to determine service generic parameters for Service: " + this.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String joinIdentifiers(Collection<Long> ids) {
|
||||
var joiner = new StringJoiner(", ");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class EntityReference extends AtomicReference<PersistenceEntity> {
|
||||
public EntityReference(PersistenceEntity initialValue) {
|
||||
super(initialValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,38 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
public class EntityReferenceDeserializer extends ValueDeserializer<PersistenceEntity> {
|
||||
import java.util.UUID;
|
||||
|
||||
public class EntityReferenceDeserializer extends StdDeserializer<EntityReference> {
|
||||
|
||||
private final RepositoryServiceImpl repositoryService;
|
||||
|
||||
public EntityReferenceDeserializer(RepositoryServiceImpl repositoryService) {
|
||||
super(EntityReference.class);
|
||||
this.repositoryService = repositoryService;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void serialize(PersistenceEntity value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException {
|
||||
// gen.writeStartObject();
|
||||
// gen.writeStringProperty("type", value.getBaseType().getName());
|
||||
// gen.writeNumberProperty("id", value.getId());
|
||||
// gen.writeStringProperty("uuid", value.getUUID().toString());
|
||||
// gen.writeEndObject();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public PersistenceEntity deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
|
||||
public EntityReference deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
|
||||
var node = ctxt.readTree(p);
|
||||
var type = node.get("type").asString();
|
||||
var id = node.get("id").asLong();
|
||||
var uuid = node.get("uuid").asString();
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
var repository = repositoryService.getRepositoryForEntity((Class<? extends PersistenceEntity>) Class.forName(type));
|
||||
if (uuid != null) {
|
||||
return new EntityReference(repository.load(UUID.fromString(uuid)));
|
||||
}
|
||||
return new EntityReference(repository.load(id));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonGenerator;
|
||||
import tools.jackson.databind.SerializationContext;
|
||||
import tools.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
public class EntityReferenceSerializer extends StdSerializer<PersistenceEntity> {
|
||||
public class EntityReferenceSerializer extends StdSerializer<EntityReference> {
|
||||
private RepositoryServiceImpl repositoryService;
|
||||
|
||||
public EntityReferenceSerializer() {
|
||||
this(PersistenceEntity.class);
|
||||
public EntityReferenceSerializer(RepositoryServiceImpl repositoryService) {
|
||||
super(EntityReference.class);
|
||||
this.repositoryService = repositoryService;
|
||||
}
|
||||
|
||||
protected EntityReferenceSerializer(Class<PersistenceEntity> t) {
|
||||
|
|
@ -17,11 +18,17 @@ public class EntityReferenceSerializer extends StdSerializer<PersistenceEntity>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void serialize(PersistenceEntity value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException {
|
||||
public void serialize(EntityReference reference, JsonGenerator gen, SerializationContext provider) throws JacksonException {
|
||||
var value = reference.get();
|
||||
if(value == null) {
|
||||
gen.writeNull();
|
||||
return;
|
||||
}
|
||||
gen.writeStartObject();
|
||||
gen.writeStringProperty("type", value.getBaseType().getName());
|
||||
var baseType = repositoryService.getEntityBaseType(value.getClass());
|
||||
gen.writeStringProperty("type", baseType.getName());
|
||||
gen.writeNumberProperty("id", value.getId());
|
||||
gen.writeStringProperty("uuid", value.getUUID().toString());
|
||||
gen.writeStringProperty("uuid", value.getUuid().toString());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import lombok.Getter;
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.JacksonModule;
|
||||
import tools.jackson.databind.module.SimpleDeserializers;
|
||||
|
|
@ -30,7 +29,7 @@ class PersistenceSerializationModule extends JacksonModule {
|
|||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.addSerializers(new SimpleSerializers(List.of(new EntityReferenceSerializer())));
|
||||
context.addDeserializers(new SimpleDeserializers(Map.of(PersistenceEntity.class, new EntityReferenceDeserializer(repositoryService))));
|
||||
context.addSerializers(new SimpleSerializers(List.of(new EntityReferenceSerializer(repositoryService))));
|
||||
context.addDeserializers(new SimpleDeserializers(Map.of(EntityReference.class, new EntityReferenceDeserializer(repositoryService))));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.hibernate.query.Query;
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
import ru.kirillius.XCP.Persistence.NodeEntity;
|
||||
import ru.kirillius.XCP.Persistence.NodeRepository;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractNodeRepository<E extends NodeEntity> extends AbstractRepository<E> implements NodeRepository<E> {
|
||||
|
||||
public AbstractNodeRepository(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamHandler<E> getByGroup(Group group) {
|
||||
return search("WHERE group = ?1", List.of(group));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamHandler<E> getByTags(Collection<Tag> tags) {
|
||||
|
||||
var hql = "SELECT n FROM " + tableName + " n JOIN n.tags t " +
|
||||
"WHERE t.name IN :tagNames " +
|
||||
"GROUP BY n " +
|
||||
"HAVING COUNT(DISTINCT t.name) = :tagCount";
|
||||
|
||||
var session = repositoryService.openSession();
|
||||
var transaction = session.beginTransaction();
|
||||
var query = (Query<E>) session.createQuery(hql, entityImplementationClass);
|
||||
query.setParameter("tagNames", tags);
|
||||
query.setParameter("tagCount", tags.size());
|
||||
return new ResourceHandlerImpl<>(query, transaction, session);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
import ru.kirillius.XCP.Persistence.*;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
import ru.kirillius.XCP.Serialization.SerializationUtils;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@EntityImplementation(GroupRepositoryImpl.GroupEntity.class)
|
||||
public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implements GroupRepository {
|
||||
|
||||
public GroupRepositoryImpl(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamHandler<Group> getChildrenOf(Group group) {
|
||||
return search("WHERE parent = ?1", List.of(group));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamHandler<Group> getAllChildrenInHierarchy(Group group) {
|
||||
var children = new ArrayList<Group>();
|
||||
var pendingGroups = new ConcurrentLinkedQueue<Group>();
|
||||
pendingGroups.add(group);
|
||||
while (!pendingGroups.isEmpty()) {
|
||||
var child = pendingGroups.remove();
|
||||
try (var handler = getChildrenOf(child)) {
|
||||
handler.get().forEach(item -> {
|
||||
children.add(item);
|
||||
pendingGroups.add(item);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return new SimpleStreamHandler<>(children.stream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getRoot() {
|
||||
try (var handler = search("WHERE parent is null", Collections.emptyList())) {
|
||||
return handler.get().findFirst().orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to get root group", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Group entity) {
|
||||
if (entity != null && entity.getParent() == null) {
|
||||
var root = getRoot();
|
||||
if (root != null && !root.equals(entity)) {
|
||||
throw new IllegalStateException("Root group already exists");
|
||||
}
|
||||
}
|
||||
super.store(entity);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "Groups")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class GroupEntity implements Group {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@JsonProperty
|
||||
private long id = 0;
|
||||
|
||||
@JsonProperty
|
||||
@Column(unique = true, nullable = false)
|
||||
@UuidGenerator
|
||||
private UUID uuid;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String name = "";
|
||||
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Getter
|
||||
@Setter
|
||||
private String icon = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean prototype;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean protectedEntity;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean enabled;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JsonIgnore
|
||||
private GroupEntity parent;
|
||||
|
||||
@JsonProperty("parent")
|
||||
public EntityReference getParentReference() {
|
||||
return new EntityReference(getParent());
|
||||
}
|
||||
|
||||
@JsonProperty("parent")
|
||||
public void setParentReference(EntityReference entityReference) {
|
||||
parent = (GroupEntity) entityReference.get();
|
||||
}
|
||||
|
||||
public Group getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Group parent) {
|
||||
this.parent = (GroupEntity) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTags(Set<Tag> tags) {
|
||||
this.tags = tags.stream().map(t -> (TagRepositoryImpl.TagEntity) t).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Tag> getTags() {
|
||||
return new HashSet<>(tags);
|
||||
}
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Getter
|
||||
@Setter
|
||||
private ObjectNode properties = SerializationUtils.EmptyObject();
|
||||
|
||||
@JsonProperty
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
private Set<TagRepositoryImpl.TagEntity> tags = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof GroupEntity that)) return false;
|
||||
return Objects.equals(uuid, that.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import ru.kirillius.XCP.Commons.ResourceHandler;
|
||||
import ru.kirillius.XCP.Persistence.*;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@EntityImplementation(TagRepositoryImpl.TagEntity.class)
|
||||
public class TagRepositoryImpl extends AbstractRepository<Tag> implements TagRepository {
|
||||
|
||||
public TagRepositoryImpl(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Tag getByName(String name) {
|
||||
try (var handler = buildQueryParametrized("WHERE name = ?1", name)) {
|
||||
var result = handler.get().findFirst();
|
||||
return result.orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceHandler<Stream<Tag>> getByNames(Collection<String> names) {
|
||||
return search("where name IN (?1)", List.of(names));
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "Tags")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class TagEntity implements Tag {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@JsonProperty
|
||||
private long id = 0;
|
||||
|
||||
@JsonProperty
|
||||
@Column(unique = true, nullable = false)
|
||||
@UuidGenerator
|
||||
private UUID uuid;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
@JsonProperty
|
||||
private String name = "";
|
||||
private static final Pattern NAME_PATTERN =
|
||||
Pattern.compile("^[a-z0-9]+(\\.[a-z0-9]+)*$");
|
||||
|
||||
public void setName(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
throw new IllegalArgumentException("Name cannot be null or empty");
|
||||
}
|
||||
|
||||
name = name.trim().toLowerCase();
|
||||
|
||||
if (!NAME_PATTERN.matcher(name).matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Invalid name: '%s'. " +
|
||||
"Name must contain only lowercase letters a-z, digits 0-9, and dots. " +
|
||||
"Cannot start or end with dot, and dots cannot be consecutive.",
|
||||
name
|
||||
)
|
||||
);
|
||||
}
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof TagEntity tagEntity)) return false;
|
||||
return Objects.equals(name, tagEntity.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
||||
import ru.kirillius.XCP.Persistence.Entities.User;
|
||||
import ru.kirillius.XCP.Persistence.EntityImplementation;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
||||
import ru.kirillius.XCP.Security.Argon2HashUtility;
|
||||
import ru.kirillius.XCP.Security.UserRole;
|
||||
import ru.kirillius.XCP.Serialization.SerializationUtils;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@EntityImplementation(UserRepositoryImpl.UserEntity.class)
|
||||
public class UserRepositoryImpl extends AbstractRepository<User> implements UserRepository {
|
||||
|
||||
public UserRepositoryImpl(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public User getByLogin(String login) {
|
||||
try (var request = buildQueryParametrized("WHERE login = ?1", login)) {
|
||||
return request.get().findFirst().orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "Users")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class UserEntity implements User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@JsonProperty
|
||||
private long id = 0;
|
||||
|
||||
@JsonProperty
|
||||
@Column(unique = true, nullable = false)
|
||||
@UuidGenerator
|
||||
private UUID uuid;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String login = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String name = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String passwordHash = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Enumerated(EnumType.STRING)
|
||||
private UserRole role=UserRole.User;
|
||||
|
||||
@Column(name = "custom_values", nullable = false)
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonProperty
|
||||
private ObjectNode values = SerializationUtils.EmptyObject();
|
||||
|
||||
|
||||
@Override
|
||||
public void setPassword(String password) {
|
||||
passwordHash = Argon2HashUtility.getInstance().hash(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyPassword(String password) {
|
||||
return Argon2HashUtility.getInstance().validate(password, passwordHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof UserEntity that)) return false;
|
||||
return Objects.equals(uuid, that.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,12 @@ import lombok.Getter;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import ru.kirillius.XCP.api.Commons.Context;
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.Repository;
|
||||
import ru.kirillius.XCP.api.Persistence.RepositoryService;
|
||||
import ru.kirillius.XCP.Commons.Context;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
@ -22,16 +20,19 @@ public final class RepositoryServiceImpl implements RepositoryService {
|
|||
private final Configuration configuration;
|
||||
private SessionFactory sessionFactory;
|
||||
private DatabaseConfiguration databaseConfiguration;
|
||||
private final Map<Class<? extends PersistenceEntity>, Class<? extends PersistenceEntity>> entityBaseBindings = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends Repository<?>>, Class<? extends PersistenceEntity>> repositoryEntityBindings = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends Repository<?>>, Repository<?>> repositoryBindings = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends Repository<?>>, Class<? extends Repository<?>>> repositoryBaseBindings = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends PersistenceEntity>, Class<? extends Repository<?>>> entityBindings = new ConcurrentHashMap<>();
|
||||
|
||||
private final Collection<Class<? extends AbstractRepository<?>>> managedRepositoryClasses;
|
||||
private Context context;
|
||||
|
||||
public RepositoryServiceImpl(DatabaseConfiguration databaseConfiguration, Collection<Class<? extends AbstractRepository<?>>> repositoryImplClasses) {
|
||||
managedRepositoryClasses = repositoryImplClasses;
|
||||
configuration = new Configuration();
|
||||
configuration.configure();
|
||||
registerClasses(repositoryImplClasses);
|
||||
registerClasses();
|
||||
loadDatabaseConfig(databaseConfiguration);
|
||||
}
|
||||
|
||||
|
|
@ -44,10 +45,10 @@ public final class RepositoryServiceImpl implements RepositoryService {
|
|||
managedRepositoryClasses = repositoryImplClasses;
|
||||
configuration = new Configuration();
|
||||
configuration.configure();
|
||||
registerClasses(repositoryImplClasses);
|
||||
registerClasses();
|
||||
}
|
||||
|
||||
private void registerClasses(Collection<Class<? extends AbstractRepository<?>>> repositoryImplClasses) {
|
||||
private void registerClasses() {
|
||||
managedRepositoryClasses.forEach(aClass -> {
|
||||
var implementation = aClass.getAnnotation(EntityImplementation.class);
|
||||
if (implementation == null) {
|
||||
|
|
@ -88,14 +89,16 @@ public final class RepositoryServiceImpl implements RepositoryService {
|
|||
throw new IllegalStateException("Initialized already");
|
||||
}
|
||||
initialized = true;
|
||||
this.context = context;
|
||||
if (databaseConfiguration == null) {
|
||||
loadDatabaseConfig(new H2DatabaseInFileConfiguration(context.getConfig().getDatabaseFile()));
|
||||
}
|
||||
managedRepositoryClasses.forEach(aClass -> {
|
||||
var instance = instantiateRepository(aClass);
|
||||
var baseClass = instance.getBaseClass();
|
||||
var baseClass = getRepositoryBaseType(aClass);
|
||||
repositoryBindings.put(baseClass, instance);
|
||||
var entityClass = instance.getEntityClass();
|
||||
|
||||
var entityClass = getEntityBaseType(getRepositoryEntityType(aClass));
|
||||
entityBindings.put(entityClass, baseClass);
|
||||
});
|
||||
mapper = JsonMapper.builder().addModule(new PersistenceSerializationModule(this)).build();
|
||||
|
|
@ -113,4 +116,62 @@ public final class RepositoryServiceImpl implements RepositoryService {
|
|||
//noinspection unchecked
|
||||
return (R) repositoryBindings.get(repositoryType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entity base interface Class<E> from Class<? extends E>
|
||||
* @param entityClass
|
||||
* @return Class<E>
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends PersistenceEntity> getEntityBaseType(Class<? extends PersistenceEntity> entityClass) {
|
||||
if (!entityBaseBindings.containsKey(entityClass)) {
|
||||
var foundClass = Arrays.stream(entityClass.getInterfaces()).filter(PersistenceEntity.class::isAssignableFrom).findFirst();
|
||||
if (foundClass.isPresent()) {
|
||||
entityBaseBindings.put(entityClass, (Class<? extends PersistenceEntity>) foundClass.get());
|
||||
} else {
|
||||
throw new RuntimeException("Unable to determine base interface Class<? extends PersistenceEntity> of " + entityClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return entityBaseBindings.get(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns repository base interface type Class<E> from Class<? extends E>
|
||||
* @param repositoryClass
|
||||
* @return Class<E>
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Repository<?>> getRepositoryBaseType(Class<? extends Repository<?>> repositoryClass) {
|
||||
if (!repositoryBaseBindings.containsKey(repositoryClass)) {
|
||||
var foundClass = Arrays.stream(repositoryClass.getInterfaces()).filter(Repository.class::isAssignableFrom).findFirst();
|
||||
if (foundClass.isPresent()) {
|
||||
repositoryBaseBindings.put(repositoryClass, (Class<? extends Repository<?>>) foundClass.get());
|
||||
} else {
|
||||
throw new RuntimeException("Unable to determine base interface Class<? extends Repository> of " + repositoryClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return repositoryBaseBindings.get(repositoryClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Entity implementation class that implements E from Class<? extends Repository<E>>
|
||||
* @param repositoryImplClass
|
||||
* @return Class<? extends E>
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends PersistenceEntity> getRepositoryEntityType(Class<? extends Repository<?>> repositoryImplClass) {
|
||||
if (!repositoryEntityBindings.containsKey(repositoryImplClass)) {
|
||||
var annotation = repositoryImplClass.getAnnotation(EntityImplementation.class);
|
||||
if (annotation != null) {
|
||||
repositoryEntityBindings.put(repositoryImplClass, annotation.value());
|
||||
} else {
|
||||
throw new RuntimeException("Unable to get @" + EntityImplementation.class.getSimpleName() + " from class " + repositoryImplClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return repositoryEntityBindings.get(repositoryImplClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
package ru.kirillius.XCP.Persistence.Services;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
||||
import ru.kirillius.XCP.Persistence.EntityImplementation;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
|
||||
import ru.kirillius.XCP.Serialization.SerializationUtils;
|
||||
import ru.kirillius.XCP.api.Persistence.Entities.User;
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.Repositories.UserRepository;
|
||||
import ru.kirillius.XCP.api.Persistence.Repository;
|
||||
import ru.kirillius.XCP.api.Security.UserRole;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.UUID;
|
||||
|
||||
@EntityImplementation(UserRepositoryImpl.UserEntity.class)
|
||||
public class UserRepositoryImpl extends AbstractRepository<User> implements UserRepository {
|
||||
|
||||
public UserRepositoryImpl(RepositoryServiceImpl repositoryService) {
|
||||
super(repositoryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Repository<User>> getBaseClass() {
|
||||
return UserRepository.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByLoginAndPassword(String login, String password) {
|
||||
var user = (UserEntity) getByLogin(login);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
if (user.getPasswordHash().equals(UserEntity.hash(password, user.getPasswordHashSalt()))) {
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByLogin(String login) {
|
||||
try (var request = buildQueryParametrized("WHERE login = ?1", login)) {
|
||||
return request.get().findFirst().orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "UserEntity")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class UserEntity implements User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@JsonProperty
|
||||
private long id = 0;
|
||||
|
||||
@JsonProperty
|
||||
@Column(unique = true, nullable = false)
|
||||
@UuidGenerator
|
||||
private UUID UUID;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String login;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String name = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String passwordHash;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
private String passwordHashSalt;
|
||||
|
||||
@Column(nullable = false)
|
||||
@JsonProperty
|
||||
@Enumerated(EnumType.STRING)
|
||||
private UserRole role;
|
||||
|
||||
@Column(name = "custom_values", nullable = false)
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonProperty
|
||||
private ObjectNode values = SerializationUtils.EmptyObject();
|
||||
|
||||
public static String hash(String data, String salt) {
|
||||
String generatedPassword;
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-512");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
md.update(salt.getBytes(StandardCharsets.UTF_8));
|
||||
var bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
|
||||
var sb = new StringBuilder();
|
||||
for (byte aByte : bytes) {
|
||||
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
generatedPassword = sb.toString();
|
||||
return generatedPassword;
|
||||
}
|
||||
|
||||
public void changePassword(String newPass) {
|
||||
passwordHashSalt = java.util.UUID.randomUUID().toString();
|
||||
passwordHash = hash(newPass, passwordHashSalt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends PersistenceEntity> getBaseType() {
|
||||
return User.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.Commons.StreamHandler;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SimpleStreamHandler<T> implements StreamHandler<T> {
|
||||
private final Stream<T> stream;
|
||||
|
||||
public SimpleStreamHandler(Stream<T> stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> get() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import ru.kirillius.XCP.api.Commons.Config;
|
||||
import ru.kirillius.XCP.api.Commons.ConfigManager;
|
||||
import ru.kirillius.XCP.api.Commons.Context;
|
||||
import ru.kirillius.XCP.api.Commons.Service;
|
||||
|
||||
public class TestContext implements Context {
|
||||
@Override
|
||||
public Config getConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigManager getConfigManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends Service> S getService(Class<S> serviceClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.Persistence.AbstractRepository;
|
||||
import ru.kirillius.XCP.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static ru.kirillius.XCP.Persistence.TestEnvironment.instantiateTestService;
|
||||
|
||||
abstract class GenericRepositoryTest<E extends PersistenceEntity, R extends AbstractRepository<E>> {
|
||||
|
||||
protected Class<E> entityClass;
|
||||
protected Class<R> repositoryClass;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public GenericRepositoryTest() {
|
||||
try {
|
||||
var parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
|
||||
var typeArguments = parameterizedType.getActualTypeArguments();
|
||||
|
||||
if (typeArguments.length != 2) {
|
||||
throw new IllegalStateException("Generic parameters count is unsupported");
|
||||
}
|
||||
entityClass = (Class<E>) typeArguments[0];
|
||||
repositoryClass = (Class<R>) typeArguments[1];
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to determine service generic parameters for Service: " + this.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected RepositoryService spawnRepositoryService() {
|
||||
return instantiateTestService(List.of(repositoryClass));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
assertThat(entity).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStore() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
repository.store(entity);
|
||||
assertThat(entity.getId()).isNotZero();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoad() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
repository.store(entity);
|
||||
var loaded = repository.load(entity.getId());
|
||||
assertThat(loaded).isNotNull().isEqualTo(entity);
|
||||
|
||||
var loadedByUUID = repository.load(entity.getUuid());
|
||||
assertThat(loadedByUUID).isNotNull().isEqualTo(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModify() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
repository.store(entity);
|
||||
modify(entity);
|
||||
repository.store(entity);
|
||||
var loaded = repository.load(entity.getId());
|
||||
assertThat(loaded).isNotNull().isEqualTo(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemove() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
repository.store(entity);
|
||||
assertThat(repository.getCount()).isEqualTo(1);
|
||||
repository.remove(entity);
|
||||
assertThat(repository.getCount()).isZero();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSerialize() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepositoryForEntity(entityClass);
|
||||
var entity = repository.create();
|
||||
repository.store(entity);
|
||||
|
||||
var serialized = repository.serialize(entity);
|
||||
var deserialized = repository.deserialize(serialized);
|
||||
|
||||
assertThat(deserialized).isNotNull().isEqualTo(entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void modify(E entity);
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Group;
|
||||
import ru.kirillius.XCP.Persistence.RepositoryService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static ru.kirillius.XCP.Persistence.TestEnvironment.instantiateTestService;
|
||||
|
||||
class GroupRepositoryImplTest extends GenericRepositoryTest<Group, GroupRepositoryImpl> {
|
||||
@Override
|
||||
protected RepositoryService spawnRepositoryService() {
|
||||
return instantiateTestService(List.of(repositoryClass, TagRepositoryImpl.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getChildrenOf() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(GroupRepository.class);
|
||||
assertThat(repository.getRoot()).isNull();
|
||||
var root = repository.create();
|
||||
repository.store(root);
|
||||
|
||||
var anotherParent = repository.create();
|
||||
anotherParent.setParent(root);
|
||||
repository.store(anotherParent);
|
||||
|
||||
var children = new ArrayList<Group>();
|
||||
for (var i = 0; i < 20; i++) {
|
||||
var child = repository.create();
|
||||
child.setParent(anotherParent);
|
||||
children.add(child);
|
||||
repository.store(child);
|
||||
}
|
||||
|
||||
try (var handler = repository.getChildrenOf(root)) {
|
||||
assertThat(handler.get().toList()).containsExactly(anotherParent);
|
||||
}
|
||||
|
||||
try (var handler = repository.getChildrenOf(anotherParent)) {
|
||||
assertThat(handler.get().toList()).containsExactlyElementsOf(children);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllChildrenInHierarchy() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(GroupRepository.class);
|
||||
assertThat(repository.getRoot()).isNull();
|
||||
var root = repository.create();
|
||||
repository.store(root);
|
||||
|
||||
var anotherParent = repository.create();
|
||||
anotherParent.setParent(root);
|
||||
repository.store(anotherParent);
|
||||
|
||||
var children = new ArrayList<Group>();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var child = repository.create();
|
||||
child.setParent(anotherParent);
|
||||
children.add(child);
|
||||
repository.store(child);
|
||||
|
||||
var subchild = repository.create();
|
||||
subchild.setParent(child);
|
||||
repository.store(subchild);
|
||||
children.add(subchild);
|
||||
}
|
||||
|
||||
try (var handler = repository.getAllChildrenInHierarchy(anotherParent)) {
|
||||
assertThat(handler.get().toList()).containsExactlyInAnyOrderElementsOf(children);
|
||||
}
|
||||
|
||||
try (var handler = repository.getAllChildrenInHierarchy(root)) {
|
||||
children.add(anotherParent);
|
||||
assertThat(handler.get().toList()).containsExactlyInAnyOrderElementsOf(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRoot() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(GroupRepository.class);
|
||||
assertThat(repository.getRoot()).isNull();
|
||||
var root = repository.create();
|
||||
repository.store(root);
|
||||
var notARoot = repository.create();
|
||||
notARoot.setParent(root);
|
||||
repository.store(notARoot);
|
||||
assertThat(repository.getRoot()).isNotNull().isEqualTo(root);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("CatchMayIgnoreException")
|
||||
@Test
|
||||
void testManyRoots() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(GroupRepository.class);
|
||||
var root = repository.create();
|
||||
var secondaryRoot = repository.create();
|
||||
|
||||
try {
|
||||
repository.store(List.of(root, secondaryRoot));
|
||||
throw new Exception("Nothing is thrown");
|
||||
} catch (Throwable e) {
|
||||
assertThat(e).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(Group entity) {
|
||||
entity.setName(UUID.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.Persistence.Entities.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TagRepositoryImplTest extends GenericRepositoryTest<Tag, TagRepositoryImpl> {
|
||||
@Test
|
||||
void testGetByName() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(TagRepository.class);
|
||||
var tag = repository.create();
|
||||
tag.setName("test");
|
||||
repository.store(tag);
|
||||
var found = repository.getByName(tag.getName());
|
||||
assertThat(found).isEqualTo(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(Tag entity) {
|
||||
entity.setName("test" + Math.random());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package ru.kirillius.XCP.Persistence.Repositories;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.Persistence.Entities.User;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UserRepositoryImplTest extends GenericRepositoryTest<User, UserRepositoryImpl> {
|
||||
|
||||
@Test
|
||||
void getByLogin() throws IOException {
|
||||
var correct = "correctlogin";
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(UserRepository.class);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var user = repository.create();
|
||||
user.setLogin("incorrect" + UUID.randomUUID());
|
||||
repository.store(user);
|
||||
}
|
||||
var correctUser = repository.create();
|
||||
correctUser.setLogin(correct);
|
||||
repository.store(correctUser);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var user = repository.create();
|
||||
user.setLogin("incorrect" + UUID.randomUUID());
|
||||
repository.store(user);
|
||||
}
|
||||
|
||||
var loaded = repository.getByLogin(correct);
|
||||
assertThat(loaded).isNotNull().isEqualTo(correctUser);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswords() throws IOException {
|
||||
try (var service = spawnRepositoryService()) {
|
||||
var repository = service.getRepository(UserRepository.class);
|
||||
var user = repository.create();
|
||||
var randPass = UUID.randomUUID().toString();
|
||||
user.setPassword(randPass);
|
||||
|
||||
assertThat(user.verifyPassword(randPass)).isTrue();
|
||||
assertThat(((UserRepositoryImpl.UserEntity) user).getPasswordHash()).doesNotContain(randPass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(User entity) {
|
||||
entity.setName("test" + UUID.randomUUID());
|
||||
}
|
||||
}
|
||||
|
|
@ -6,22 +6,17 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.kirillius.XCP.api.Commons.Context;
|
||||
import ru.kirillius.XCP.api.Persistence.PersistenceEntity;
|
||||
import ru.kirillius.XCP.api.Persistence.Repository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static ru.kirillius.XCP.Persistence.TestEnvironment.instantiateTestService;
|
||||
|
||||
class RepositoryServiceImplTest {
|
||||
private RepositoryServiceImpl instantiateTestService(Collection<Class<? extends AbstractRepository<?>>> classes) {
|
||||
var service = new RepositoryServiceImpl(new H2InMemoryConfiguration(), classes);
|
||||
service.initialize(mock(Context.class));
|
||||
return service;
|
||||
}
|
||||
|
||||
public interface TestEntity extends PersistenceEntity {
|
||||
String getTestField();
|
||||
|
|
@ -39,11 +34,6 @@ class RepositoryServiceImplTest {
|
|||
super(repositoryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Repository<TestEntity>> getBaseClass() {
|
||||
return TestRepository.class;
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table
|
||||
public static class EntityImpl implements TestEntity {
|
||||
|
|
@ -64,22 +54,18 @@ class RepositoryServiceImplTest {
|
|||
@UuidGenerator
|
||||
@Getter
|
||||
@Setter
|
||||
private UUID UUID;
|
||||
private UUID uuid;
|
||||
|
||||
@Override
|
||||
public Class<? extends PersistenceEntity> getBaseType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof EntityImpl entity)) return false;
|
||||
return id == entity.id && Objects.equals(testField, entity.testField) && Objects.equals(UUID, entity.UUID);
|
||||
return id == entity.id && Objects.equals(testField, entity.testField) && Objects.equals(uuid, entity.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(testField, id, UUID);
|
||||
return Objects.hash(testField, id, uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,6 +87,18 @@ class RepositoryServiceImplTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void TestSerialization() {
|
||||
try (var service = instantiateTestService(List.of(RepoImpl.class))) {
|
||||
var repository = service.getRepository(TestRepository.class);
|
||||
var testEntity = repository.create();
|
||||
repository.store(testEntity);
|
||||
var serialized = repository.serialize(testEntity);
|
||||
var deserialized = repository.deserialize(serialized);
|
||||
assertThat(deserialized).isEqualTo(testEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestEntityStore() {
|
||||
try (var service = instantiateTestService(List.of(RepoImpl.class))) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package ru.kirillius.XCP.Persistence;
|
||||
|
||||
import ru.kirillius.XCP.Commons.Context;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class TestEnvironment {
|
||||
public static RepositoryServiceImpl instantiateTestService(Collection<Class<? extends AbstractRepository<?>>> classes) {
|
||||
var service = new RepositoryServiceImpl(new H2InMemoryConfiguration(), classes);
|
||||
service.initialize(mock(Context.class));
|
||||
return service;
|
||||
}
|
||||
}
|
||||
8
pom.xml
8
pom.xml
|
|
@ -11,6 +11,7 @@
|
|||
<modules>
|
||||
<module>api</module>
|
||||
<module>database</module>
|
||||
<module>core</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
|
@ -79,6 +80,13 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/de.mkammerer/argon2-jvm -->
|
||||
<dependency>
|
||||
<groupId>de.mkammerer</groupId>
|
||||
<artifactId>argon2-jvm</artifactId>
|
||||
<version>2.12</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
|
|
|
|||
Loading…
Reference in New Issue