HHH-17507 HHH-17574 Fixed wrap() & unwrap() & updated unit tests
This commit is contained in:
parent
2521e72d37
commit
6768e14f8c
|
@ -39,6 +39,9 @@ public class CurrencyJavaType extends AbstractClassJavaType<Currency> {
|
||||||
if ( value == null ) {
|
if ( value == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if ( Currency.class.isAssignableFrom( type ) ) {
|
||||||
|
return (X) value;
|
||||||
|
}
|
||||||
if ( String.class.isAssignableFrom( type ) ) {
|
if ( String.class.isAssignableFrom( type ) ) {
|
||||||
return (X) value.getCurrencyCode();
|
return (X) value.getCurrencyCode();
|
||||||
}
|
}
|
||||||
|
@ -50,6 +53,9 @@ public class CurrencyJavaType extends AbstractClassJavaType<Currency> {
|
||||||
if ( value == null ) {
|
if ( value == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if ( value instanceof Currency ) {
|
||||||
|
return (Currency) value;
|
||||||
|
}
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
return Currency.getInstance( (String) value );
|
return Currency.getInstance( (String) value );
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,10 @@ public class YearJavaType extends AbstractClassJavaType<Year> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( value instanceof Year) {
|
||||||
|
return (Year) value;
|
||||||
|
}
|
||||||
|
|
||||||
if ( value instanceof Number ) {
|
if ( value instanceof Number ) {
|
||||||
return Year.of( ( (Number) value ).intValue() );
|
return Year.of( ( (Number) value ).intValue() );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,39 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.mapping.basic;
|
package org.hibernate.orm.test.mapping.basic;
|
||||||
|
|
||||||
import java.sql.Types;
|
import jakarta.persistence.ElementCollection;
|
||||||
import java.util.Currency;
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.engine.jdbc.LobCreator;
|
||||||
|
import org.hibernate.engine.jdbc.NonContextualLobCreator;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
||||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||||
|
import org.hibernate.orm.test.mapping.type.java.YearMappingTests;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.CurrencyJavaType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.Year;
|
||||||
|
import java.util.Currency;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@ -31,6 +47,7 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
*/
|
*/
|
||||||
@DomainModel(annotatedClasses = CurrencyMappingTests.EntityWithCurrency.class)
|
@DomainModel(annotatedClasses = CurrencyMappingTests.EntityWithCurrency.class)
|
||||||
@SessionFactory
|
@SessionFactory
|
||||||
|
@JiraKey("HHH-17574")
|
||||||
public class CurrencyMappingTests {
|
public class CurrencyMappingTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -46,17 +63,129 @@ public class CurrencyMappingTests {
|
||||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Currency.class));
|
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Currency.class));
|
||||||
assertThat( jdbcMapping.getJdbcType(), equalTo( jdbcRegistry.getDescriptor( Types.VARCHAR)));
|
assertThat( jdbcMapping.getJdbcType(), equalTo( jdbcRegistry.getDescriptor( Types.VARCHAR)));
|
||||||
|
|
||||||
|
final EntityWithCurrency entity = createEntityWithCurrency();
|
||||||
|
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
(session) -> {
|
(session) -> session.persist(entity)
|
||||||
session.persist(new EntityWithCurrency(1, Currency.getInstance("USD")));
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
(session) -> session.find(EntityWithCurrency.class, 1)
|
(session) -> session.find(EntityWithCurrency.class, 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicAssertions(final SessionFactoryScope scope) {
|
||||||
|
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
|
||||||
|
final JdbcTypeRegistry jdbcTypeRegistry = sessionFactory.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
|
final EntityPersister entityDescriptor = sessionFactory.getMappingMetamodel().getEntityDescriptor(
|
||||||
|
EntityWithCurrency.class );
|
||||||
|
{
|
||||||
|
final BasicAttributeMapping currencyAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping(
|
||||||
|
"currency");
|
||||||
|
Assertions.assertThat(currencyAttribute.getJdbcMapping().getJdbcType())
|
||||||
|
.isEqualTo(jdbcTypeRegistry.getDescriptor(Types.VARCHAR));
|
||||||
|
Assertions.assertThat(currencyAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass()).isEqualTo(
|
||||||
|
Currency.class );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final PluralAttributeMapping currenciesAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping(
|
||||||
|
"currencies" );
|
||||||
|
final BasicValuedCollectionPart elementDescriptor = (BasicValuedCollectionPart) currenciesAttribute.getElementDescriptor();
|
||||||
|
Assertions.assertThat( elementDescriptor.getJdbcMapping().getJdbcType() )
|
||||||
|
.isEqualTo( jdbcTypeRegistry.getDescriptor( Types.VARCHAR ) );
|
||||||
|
Assertions.assertThat( elementDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo(
|
||||||
|
Currency.class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnwrapPass() {
|
||||||
|
final CurrencyJavaType currencyJavaType = new CurrencyJavaType();
|
||||||
|
final Currency currency = Currency.getInstance("CHF");
|
||||||
|
{
|
||||||
|
final Currency c = currencyJavaType.unwrap(currency, Currency.class, null);
|
||||||
|
Assertions.assertThat( c ).isEqualTo( currency );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final String c = currencyJavaType.unwrap(currency, String.class, null);
|
||||||
|
Assertions.assertThat( c ).isEqualTo( "CHF" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnwrapFail() {
|
||||||
|
final CurrencyJavaType currencyJavaType = new CurrencyJavaType();
|
||||||
|
final Currency currency = Currency.getInstance("CHF");
|
||||||
|
{
|
||||||
|
Assertions.assertThatThrownBy( () ->
|
||||||
|
currencyJavaType.unwrap(currency, Boolean.class, null)
|
||||||
|
).isInstanceOf( HibernateException.class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrapPass() {
|
||||||
|
final CurrencyJavaType currencyJavaType = new CurrencyJavaType();
|
||||||
|
{
|
||||||
|
final Currency usingNull = currencyJavaType.wrap(null, null);
|
||||||
|
Assertions.assertThat(usingNull).isNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Currency usingString = currencyJavaType.wrap("CHF", null);
|
||||||
|
Assertions.assertThat(usingString).isNotNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Currency usingCurrency = currencyJavaType.wrap(Currency.getInstance("CHF"), null);
|
||||||
|
Assertions.assertThat(usingCurrency).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrapFail() {
|
||||||
|
final CurrencyJavaType currencyJavaType = new CurrencyJavaType();
|
||||||
|
{
|
||||||
|
final String usingEmptyString = "";
|
||||||
|
Assertions.assertThatThrownBy(() ->
|
||||||
|
currencyJavaType.wrap(usingEmptyString, null)
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Integer usingInteger = Integer.valueOf(269);
|
||||||
|
Assertions.assertThatThrownBy(() ->
|
||||||
|
currencyJavaType.wrap(usingInteger, null)
|
||||||
|
).isInstanceOf(HibernateException.class);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final CurrencyJavaType usingSelf = new CurrencyJavaType();
|
||||||
|
Assertions.assertThatThrownBy(() ->
|
||||||
|
currencyJavaType.wrap(usingSelf, null)
|
||||||
|
).isInstanceOf(HibernateException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsage(final SessionFactoryScope scope) {
|
||||||
|
final EntityWithCurrency entity = createEntityWithCurrency();
|
||||||
|
scope.inTransaction((session) -> session.persist(entity));
|
||||||
|
try {
|
||||||
|
scope.inTransaction(session -> session.createQuery("from EntityWithCurrency", EntityWithCurrency.class).list());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
scope.inTransaction( session -> session.remove( entity ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EntityWithCurrency createEntityWithCurrency() {
|
||||||
|
final Currency currency = Currency.getInstance("USD");
|
||||||
|
|
||||||
|
final Set<Currency> currencies = new HashSet<>();
|
||||||
|
currencies.add(Currency.getInstance("CHF"));
|
||||||
|
currencies.add(Currency.getInstance("EUR"));
|
||||||
|
|
||||||
|
return new EntityWithCurrency( 1, currency, currencies );
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "EntityWithCurrency")
|
@Entity(name = "EntityWithCurrency")
|
||||||
@Table(name = "EntityWithCurrency")
|
@Table(name = "EntityWithCurrency")
|
||||||
public static class EntityWithCurrency {
|
public static class EntityWithCurrency {
|
||||||
|
@ -68,12 +197,18 @@ public class CurrencyMappingTests {
|
||||||
private Currency currency;
|
private Currency currency;
|
||||||
//end::basic-Currency-example[]
|
//end::basic-Currency-example[]
|
||||||
|
|
||||||
public EntityWithCurrency() {
|
@ElementCollection
|
||||||
|
private Set<Currency> currencies;
|
||||||
|
|
||||||
|
public EntityWithCurrency() {
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityWithCurrency(Integer id, Currency currency) {
|
public EntityWithCurrency(final Integer id, final Currency currency, final Set<Currency> currencies) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
}
|
this.currencies = currencies;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,40 +6,33 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.mapping.type.java;
|
package org.hibernate.orm.test.mapping.type.java;
|
||||||
|
|
||||||
import java.sql.Types;
|
import jakarta.persistence.*;
|
||||||
import java.time.Year;
|
import org.assertj.core.api.Assertions;
|
||||||
import java.util.HashMap;
|
import org.hibernate.HibernateException;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.JiraKey;
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.YearJavaType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import jakarta.persistence.CollectionTable;
|
import java.sql.Types;
|
||||||
import jakarta.persistence.Column;
|
import java.time.Year;
|
||||||
import jakarta.persistence.ElementCollection;
|
import java.time.format.DateTimeParseException;
|
||||||
import jakarta.persistence.Entity;
|
import java.util.*;
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.JoinColumn;
|
|
||||||
import jakarta.persistence.MapKeyColumn;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@DomainModel( annotatedClasses = YearMappingTests.YearMappingTestEntity.class )
|
@DomainModel( annotatedClasses = YearMappingTests.YearMappingTestEntity.class )
|
||||||
@SessionFactory
|
@SessionFactory
|
||||||
@JiraKey( "HHH-10558" )
|
@JiraKey( "HHH-10558" )
|
||||||
|
@JiraKey( "HHH-17507" )
|
||||||
public class YearMappingTests {
|
public class YearMappingTests {
|
||||||
@Test
|
@Test
|
||||||
public void basicAssertions(SessionFactoryScope scope) {
|
public void basicAssertions(SessionFactoryScope scope) {
|
||||||
|
@ -69,20 +62,98 @@ public class YearMappingTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUsage(SessionFactoryScope scope) {
|
public void testUsage(SessionFactoryScope scope) {
|
||||||
final YearMappingTestEntity entity = new YearMappingTestEntity( 1, "one", Year.now() );
|
final YearMappingTestEntity entity1 = new YearMappingTestEntity( 1, "one", Year.now() );
|
||||||
final YearMappingTestEntity entity2 = new YearMappingTestEntity( 2, "two", Year.parse( "+10000" ) );
|
final YearMappingTestEntity entity2 = new YearMappingTestEntity( 2, "two", Year.parse( "+10000" ) );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
session.save( entity );
|
session.persist( entity1 );
|
||||||
session.save( entity2 );
|
session.persist( entity2 );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
scope.inTransaction( (session) -> session.createQuery( "from YearMappingTestEntity" ).list() );
|
scope.inTransaction( (session) -> session.createQuery( "from YearMappingTestEntity", YearMappingTestEntity.class ).list() );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
scope.inTransaction( session -> session.delete( entity ) );
|
scope.inTransaction( session -> session.remove( entity1 ) );
|
||||||
scope.inTransaction( session -> session.delete( entity2 ) );
|
scope.inTransaction( session -> session.remove( entity2 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testUnwrapPass() {
|
||||||
|
final YearJavaType yearJavaType = new YearJavaType();
|
||||||
|
final Year year = Year.of(1943);
|
||||||
|
{
|
||||||
|
final Year y = yearJavaType.unwrap(year, Year.class, null);
|
||||||
|
Assertions.assertThat( y ).isEqualTo( year );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Integer y = yearJavaType.unwrap(year, Integer.class, null);
|
||||||
|
Assertions.assertThat( y ).isEqualTo( Integer.valueOf( 1943 ) );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Long y = yearJavaType.unwrap(year, Long.class, null);
|
||||||
|
Assertions.assertThat( y ).isEqualTo( Long.valueOf( 1943L ) );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final String y = yearJavaType.unwrap(year, String.class, null);
|
||||||
|
Assertions.assertThat( y ).isEqualTo( "1943" );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Object y = yearJavaType.unwrap(year, Object.class, null);
|
||||||
|
Assertions.assertThat( y.toString() ).isEqualTo( "1943" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnwrapFail() {
|
||||||
|
final YearJavaType yearJavaType = new YearJavaType();
|
||||||
|
final Year year = Year.of(1943);
|
||||||
|
{
|
||||||
|
Assertions.assertThatThrownBy( () ->
|
||||||
|
yearJavaType.unwrap(year, Boolean.class, null)
|
||||||
|
).isInstanceOf( HibernateException.class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrapPass() {
|
||||||
|
final YearJavaType yearJavaType = new YearJavaType();
|
||||||
|
{
|
||||||
|
final Year usingNull = yearJavaType.wrap( null, null );
|
||||||
|
Assertions.assertThat( usingNull ).isNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Year usingNumber = yearJavaType.wrap( 1943, null );
|
||||||
|
Assertions.assertThat( usingNumber ).isNotNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Year usingNegative = yearJavaType.wrap( -1, null );
|
||||||
|
Assertions.assertThat( usingNegative ).isNotNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Year usingString = yearJavaType.wrap( "1943", null );
|
||||||
|
Assertions.assertThat( usingString ).isNotNull();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Year usingYear = yearJavaType.wrap( Year.of( 1943), null );
|
||||||
|
Assertions.assertThat( usingYear ).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrapFail() {
|
||||||
|
final YearJavaType yearJavaType = new YearJavaType();
|
||||||
|
{
|
||||||
|
final String usingEmptyString = "";
|
||||||
|
Assertions.assertThatThrownBy(() ->
|
||||||
|
yearJavaType.wrap( usingEmptyString, null )
|
||||||
|
).isInstanceOf(DateTimeParseException.class);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Date usingDate = new Date();
|
||||||
|
Assertions.assertThatThrownBy(() ->
|
||||||
|
yearJavaType.wrap( usingDate, null )
|
||||||
|
).isInstanceOf(HibernateException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,4 +227,5 @@ public class YearMappingTests {
|
||||||
this.countByYear = countByYear;
|
this.countByYear = countByYear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue