mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-20 12:45:35 +00:00
bug 4354: added persistence for ExpeditionDeviceConfiguration objects
Change-Id: I48ec066e22259f0701eff6daee522599d90741a1
This commit is contained in:
@@ -8,7 +8,11 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Import-Package: org.osgi.framework;version="1.3.0"
|
||||
Require-Bundle: com.sap.sailing.domain.persistence,
|
||||
com.sap.sailing.expeditionconnector,
|
||||
com.sap.sailing.domain.shared.android,
|
||||
com.sap.sailing.domain.common,
|
||||
com.sap.sse.common
|
||||
com.sap.sse.common,
|
||||
com.sap.sse.mongodb,
|
||||
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
|
||||
com.sap.sailing.expeditionconnector.common,
|
||||
com.sap.sailing.server.gateway.serialization.shared.android
|
||||
Export-Package: com.sap.sailing.expeditionconnector.persistence
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
|
||||
|
||||
public interface DomainObjectFactory {
|
||||
Iterable<ExpeditionDeviceConfiguration> getExpeditionDeviceConfigurations();
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
|
||||
|
||||
public interface ExpeditionDeviceIdentifier extends DeviceIdentifier {
|
||||
/**
|
||||
* Derived from an {@link ExpeditionDeviceConfiguration#getDeviceUuid()}.
|
||||
*/
|
||||
UUID getId();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
/**
|
||||
* A device identifier used to identify an installation of the regatta tool "Expedition"
|
||||
* regarding its basic GPS information.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface ExpeditionGpsDeviceIdentifier extends ExpeditionDeviceIdentifier {
|
||||
public static final String TYPE = "EXPEDITION_GPS";
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.persistence.impl.AbstractExpeditionDeviceIdentifierImpl;
|
||||
|
||||
public class ExpeditionGpsDeviceIdentifierImpl extends AbstractExpeditionDeviceIdentifierImpl implements ExpeditionGpsDeviceIdentifier {
|
||||
private static final long serialVersionUID = -4049961972156611640L;
|
||||
|
||||
public ExpeditionGpsDeviceIdentifierImpl(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierType() {
|
||||
return ExpeditionGpsDeviceIdentifier.TYPE;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
|
||||
|
||||
public class ExpeditionGpsDeviceIdentifierJsonHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
|
||||
implements DeviceIdentifierJsonHandler {
|
||||
|
||||
@Override
|
||||
public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
|
||||
throws TransformationException {
|
||||
return deserialize((String) serialized, type, stringRepresentation);
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class ExpeditionGpsDeviceIdentifierSerializationHandler {
|
||||
private ExpeditionGpsDeviceIdentifier castIdentifier(DeviceIdentifier identifier) throws TransformationException {
|
||||
if (!(identifier instanceof ExpeditionGpsDeviceIdentifier))
|
||||
throw new TransformationException("Expected a ExpeditionGpsDeviceIdentifier, got instead: " + identifier);
|
||||
return (ExpeditionGpsDeviceIdentifier) identifier;
|
||||
}
|
||||
|
||||
public Util.Pair<String, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
|
||||
return new Util.Pair<String, String>(ExpeditionGpsDeviceIdentifier.TYPE, castIdentifier(deviceIdentifier).getId().toString());
|
||||
}
|
||||
|
||||
public DeviceIdentifier deserialize(String input, String type, String stringRep) throws TransformationException {
|
||||
try {
|
||||
return new ExpeditionGpsDeviceIdentifierImpl(UUID.fromString(input));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new TransformationException("Invalid string representation of smartphone UUID", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
/**
|
||||
* A device identifier used to identify an installation of the regatta tool "Expedition"
|
||||
* regarding its sensor data beyond the basic GPS and wind information.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface ExpeditionSensorDeviceIdentifier extends ExpeditionDeviceIdentifier {
|
||||
public static final String TYPE = "EXPEDITION_SENSOR";
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.persistence.impl.AbstractExpeditionDeviceIdentifierImpl;
|
||||
|
||||
public class ExpeditionSensorDeviceIdentifierImpl extends AbstractExpeditionDeviceIdentifierImpl implements ExpeditionSensorDeviceIdentifier {
|
||||
private static final long serialVersionUID = -4049961972156611640L;
|
||||
|
||||
public ExpeditionSensorDeviceIdentifierImpl(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierType() {
|
||||
return ExpeditionSensorDeviceIdentifier.TYPE;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class ExpeditionSensorDeviceIdentifierSerializationHandler {
|
||||
private ExpeditionSensorDeviceIdentifier castIdentifier(DeviceIdentifier identifier) throws TransformationException {
|
||||
if (!(identifier instanceof ExpeditionSensorDeviceIdentifier))
|
||||
throw new TransformationException("Expected a ExpeditionSensorDeviceIdentifier, got instead: " + identifier);
|
||||
return (ExpeditionSensorDeviceIdentifier) identifier;
|
||||
}
|
||||
|
||||
public Util.Pair<String, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
|
||||
return new Util.Pair<String, String>(ExpeditionSensorDeviceIdentifier.TYPE, castIdentifier(deviceIdentifier).getId().toString());
|
||||
}
|
||||
|
||||
public DeviceIdentifier deserialize(String input, String type, String stringRep) throws TransformationException {
|
||||
try {
|
||||
return new ExpeditionSensorDeviceIdentifierImpl(UUID.fromString(input));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new TransformationException("Invalid string representation of smartphone UUID", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
|
||||
|
||||
public interface MongoObjectFactory {
|
||||
void storeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration);
|
||||
|
||||
void removeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.persistence.impl.PersistenceFactoryImpl;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
|
||||
public interface PersistenceFactory {
|
||||
PersistenceFactory INSTANCE = new PersistenceFactoryImpl();
|
||||
|
||||
/**
|
||||
* Obtains the domain object factory using the default persistence settings from {@link MongoDBConfiguration#getDefaultConfiguration()}.
|
||||
*/
|
||||
DomainObjectFactory getDefaultDomainObjectFactory();
|
||||
|
||||
DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService);
|
||||
|
||||
/**
|
||||
* Obtains the Mongo object factory using the default persistence settings from {@link MongoDBConfiguration#getDefaultConfiguration()}.
|
||||
*/
|
||||
MongoObjectFactory getDefaultMongoObjectFactory();
|
||||
|
||||
MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionDeviceIdentifier;
|
||||
|
||||
public abstract class AbstractExpeditionDeviceIdentifierImpl implements ExpeditionDeviceIdentifier {
|
||||
private static final long serialVersionUID = -6605059302775505785L;
|
||||
private final UUID id;
|
||||
|
||||
public AbstractExpeditionDeviceIdentifierImpl(UUID id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStringRepresentation() {
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof ExpeditionDeviceIdentifier) &&
|
||||
((ExpeditionDeviceIdentifier) obj).getIdentifierType().equals(getIdentifierType()) &&
|
||||
((ExpeditionDeviceIdentifier) obj).getId().equals(getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 9478 ^ getIdentifierType().hashCode() ^ getId().hashCode();
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -10,9 +10,10 @@ import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionGpsDeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifier;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
|
||||
public class Activator implements BundleActivator {
|
||||
|
||||
@@ -36,6 +37,9 @@ public class Activator implements BundleActivator {
|
||||
Activator.context = bundleContext;
|
||||
registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionGpsDeviceIdentifierMongoHandler(), getDict(ExpeditionGpsDeviceIdentifier.TYPE)));
|
||||
registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionSensorDeviceIdentifierMongoHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
|
||||
for (CollectionNames name : CollectionNames.values()) {
|
||||
MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
public enum CollectionNames {
|
||||
EXPEDITION_DEVICE_CONFIGURATIONS;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
|
||||
import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
|
||||
|
||||
public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
private final DB db;
|
||||
|
||||
public DomainObjectFactoryImpl(DB db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ExpeditionDeviceConfiguration> getExpeditionDeviceConfigurations() {
|
||||
final List<ExpeditionDeviceConfiguration> result = new ArrayList<>();
|
||||
final DBCollection expeditionDeviceConfigurationsCollection = db.getCollection(CollectionNames.EXPEDITION_DEVICE_CONFIGURATIONS.name());
|
||||
for (final Object o : expeditionDeviceConfigurationsCollection.find()) {
|
||||
final DBObject dbo = (DBObject) o;
|
||||
final UUID uuid = (UUID) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name());
|
||||
final String name = (String) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_NAME.name());
|
||||
final Number boatIdAsNumber = (Number) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID.name());
|
||||
final Integer boatId = boatIdAsNumber == null ? null : boatIdAsNumber.intValue();
|
||||
result.add(new ExpeditionDeviceConfiguration(name, uuid, boatId));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.impl.ExpeditionGpsDeviceIdentifierSerializationHandler;
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierSerializationHandler;
|
||||
|
||||
public class ExpeditionGpsDeviceIdentifierMongoHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
|
||||
implements DeviceIdentifierMongoHandler {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.impl.ExpeditionSensorDeviceIdentifierSerializationHandler;
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierSerializationHandler;
|
||||
|
||||
public class ExpeditionSensorDeviceIdentifierMongoHandler extends ExpeditionSensorDeviceIdentifierSerializationHandler
|
||||
implements DeviceIdentifierMongoHandler {
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
public enum FieldNames {
|
||||
EXPEDITION_DEVICE_CONFIGURATION_NAME, EXPEDITION_DEVICE_CONFIGURATION_UUID, EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
|
||||
import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
|
||||
|
||||
public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
private static final Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
|
||||
private final DBCollection expeditionDeviceConfigurationsCollection;
|
||||
|
||||
public MongoObjectFactoryImpl(DB db) {
|
||||
this.expeditionDeviceConfigurationsCollection = db.getCollection(CollectionNames.EXPEDITION_DEVICE_CONFIGURATIONS.name());
|
||||
DBObject index = new BasicDBObject();
|
||||
index.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), 1);
|
||||
expeditionDeviceConfigurationsCollection.createIndex(index, new BasicDBObject("unique", true));
|
||||
}
|
||||
|
||||
private BasicDBObject getExpeditionDeviceConfigurationDBKey(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
|
||||
final BasicDBObject basicDBObject = new BasicDBObject(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), expeditionDeviceConfiguration.getDeviceUuid());
|
||||
return basicDBObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
|
||||
final BasicDBObject key = getExpeditionDeviceConfigurationDBKey(expeditionDeviceConfiguration);
|
||||
final DBObject expeditionDeviceConfigurationDBObject = new BasicDBObject();
|
||||
expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), expeditionDeviceConfiguration.getDeviceUuid());
|
||||
expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_NAME.name(), expeditionDeviceConfiguration.getName());
|
||||
expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID.name(), expeditionDeviceConfiguration.getExpeditionBoatId());
|
||||
boolean success = false;
|
||||
int attempt = 0;
|
||||
Exception lastException = null;
|
||||
while (attempt < 5 && !success) {
|
||||
try {
|
||||
expeditionDeviceConfigurationsCollection.update(key, expeditionDeviceConfigurationDBObject, /* upsert */ true, /* multi */ false, WriteConcern.SAFE);
|
||||
success = true;
|
||||
attempt++;
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
logger.log(Level.WARNING, "Exception trying to write Expedition device configuration. Trying again", e);
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
throw new RuntimeException("Couldn't store Expedition device configuration "+expeditionDeviceConfiguration, lastException);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
|
||||
expeditionDeviceConfigurationsCollection.remove(getExpeditionDeviceConfigurationDBKey(expeditionDeviceConfiguration), WriteConcern.SAFE);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
|
||||
public class PersistenceFactoryImpl implements PersistenceFactory {
|
||||
|
||||
@Override
|
||||
public DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService) {
|
||||
return new DomainObjectFactoryImpl(mongoDbService.getDB());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService) {
|
||||
return new MongoObjectFactoryImpl(mongoDbService.getDB());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainObjectFactory getDefaultDomainObjectFactory() {
|
||||
return getDomainObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoObjectFactory getDefaultMongoObjectFactory() {
|
||||
return getMongoObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user