mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix test
This commit is contained in:
parent
e54c0b26f6
commit
ea56cc567c
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.json.support;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
|
import org.dromara.hutool.core.text.CharUtil;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON格式化风格,用于格式化JSON字符串
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class JSONFormatStyle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化风格
|
||||||
|
*
|
||||||
|
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||||
|
* @return JSONFormatStyle
|
||||||
|
*/
|
||||||
|
public static JSONFormatStyle getStyle(final int indentFactor){
|
||||||
|
if(0 == indentFactor){
|
||||||
|
return JSONFormatStyle.COMPACT;
|
||||||
|
} else if(2 == indentFactor){
|
||||||
|
return JSONFormatStyle.PRETTY;
|
||||||
|
}
|
||||||
|
return new JSONFormatStyle("\n", StrUtil.repeat(CharUtil.SPACE, indentFactor), indentFactor > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认紧凑风格:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>无换行</li>
|
||||||
|
* <li>无缩进</li>
|
||||||
|
* <li>{@code ','} 和 {@code ':'}后无空格</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static final JSONFormatStyle COMPACT = new JSONFormatStyle("", "", false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认格式化风格:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>换行符:{@code "\n"}</li>
|
||||||
|
* <li>双空格缩进</li>
|
||||||
|
* <li>{@code ','} 和 {@code ':'}后加空格</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static final JSONFormatStyle PRETTY = new JSONFormatStyle("\n", " ", true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 换行符,可以是 "\n" 或 "\r\n"的一个或多个
|
||||||
|
*/
|
||||||
|
private final String newline;
|
||||||
|
/**
|
||||||
|
* 缩进符,如空格、制表符等
|
||||||
|
*/
|
||||||
|
private final String indent;
|
||||||
|
/**
|
||||||
|
* {@code ','} 和 {@code ':'}分隔符后是否加空格,加空格结果如:{"a": 1}
|
||||||
|
*/
|
||||||
|
private final boolean spaceAfterSeparators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param newline 换行符
|
||||||
|
* @param indent 缩进符
|
||||||
|
* @param spaceAfterSeparators 分隔符后是否加空格
|
||||||
|
*/
|
||||||
|
public JSONFormatStyle(final String newline, final String indent, final boolean spaceAfterSeparators) {
|
||||||
|
this.newline = Assert.notNull(newline);
|
||||||
|
this.indent = Assert.notNull(indent);;
|
||||||
|
this.spaceAfterSeparators = spaceAfterSeparators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取换行符
|
||||||
|
*
|
||||||
|
* @return 换行符
|
||||||
|
*/
|
||||||
|
public String getNewline() {
|
||||||
|
return newline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缩进符
|
||||||
|
*
|
||||||
|
* @return 缩进符
|
||||||
|
*/
|
||||||
|
public String getIndent() {
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分隔符后是否加空格
|
||||||
|
*
|
||||||
|
* @return 分隔符后是否加空格
|
||||||
|
*/
|
||||||
|
public boolean isSpaceAfterSeparators() {
|
||||||
|
return spaceAfterSeparators;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import org.dromara.hutool.json.support.InternalJSONUtil;
|
|||||||
import org.dromara.hutool.json.JSON;
|
import org.dromara.hutool.json.JSON;
|
||||||
import org.dromara.hutool.json.JSONConfig;
|
import org.dromara.hutool.json.JSONConfig;
|
||||||
import org.dromara.hutool.json.JSONException;
|
import org.dromara.hutool.json.JSONException;
|
||||||
|
import org.dromara.hutool.json.support.JSONFormatStyle;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.Flushable;
|
import java.io.Flushable;
|
||||||
@ -65,17 +66,17 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
*
|
*
|
||||||
* @param appendable {@link Appendable}
|
* @param appendable {@link Appendable}
|
||||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
||||||
* @param indent 本级别缩进量
|
* @param level 层级
|
||||||
* @param config JSON选项
|
* @param config JSON选项
|
||||||
* @param predicate predicate 字段过滤器
|
* @param predicate predicate 字段过滤器
|
||||||
* @return JSONWriter
|
* @return JSONWriter
|
||||||
*/
|
*/
|
||||||
public static JSONWriter of(final Appendable appendable,
|
public static JSONWriter of(final Appendable appendable,
|
||||||
final int indentFactor,
|
final int indentFactor,
|
||||||
final int indent,
|
final int level,
|
||||||
final JSONConfig config,
|
final JSONConfig config,
|
||||||
final Predicate<MutableEntry<Object, Object>> predicate) {
|
final Predicate<MutableEntry<Object, Object>> predicate) {
|
||||||
return new JSONWriter(appendable, indentFactor, indent, config, predicate);
|
return new JSONWriter(appendable, JSONFormatStyle.getStyle(indentFactor), level, config, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,9 +97,9 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
private final JSONConfig config;
|
private final JSONConfig config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缩进因子,定义每一级别增加的缩进量
|
* 格式化风格
|
||||||
*/
|
*/
|
||||||
private final int indentFactor;
|
private final JSONFormatStyle formatStyle;
|
||||||
/**
|
/**
|
||||||
* 键值对过滤器,用于修改键值对
|
* 键值对过滤器,用于修改键值对
|
||||||
*/
|
*/
|
||||||
@ -110,27 +111,27 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
*/
|
*/
|
||||||
private boolean needSeparator;
|
private boolean needSeparator;
|
||||||
/**
|
/**
|
||||||
* 本级别缩进量
|
* 层级
|
||||||
*/
|
*/
|
||||||
private int indent;
|
private int level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param appendable {@link Appendable}
|
* @param appendable {@link Appendable}
|
||||||
* @param indentFactor 缩进因子,定义每一级别增加的缩进量
|
* @param formatStyle 格式化风格
|
||||||
* @param indent 本级别缩进量
|
* @param level 层级
|
||||||
* @param config JSON选项
|
* @param config JSON选项
|
||||||
* @param predicate 字段过滤器
|
* @param predicate 字段过滤器
|
||||||
*/
|
*/
|
||||||
public JSONWriter(final Appendable appendable,
|
public JSONWriter(final Appendable appendable,
|
||||||
final int indentFactor,
|
final JSONFormatStyle formatStyle,
|
||||||
final int indent,
|
final int level,
|
||||||
final JSONConfig config,
|
final JSONConfig config,
|
||||||
final Predicate<MutableEntry<Object, Object>> predicate) {
|
final Predicate<MutableEntry<Object, Object>> predicate) {
|
||||||
this.appendable = appendable;
|
this.appendable = appendable;
|
||||||
this.indentFactor = indentFactor;
|
this.formatStyle = formatStyle;
|
||||||
this.indent = indent;
|
this.level = level;
|
||||||
this.config = ObjUtil.defaultIfNull(config, JSONConfig::of);
|
this.config = ObjUtil.defaultIfNull(config, JSONConfig::of);
|
||||||
this.predicate = predicate;
|
this.predicate = predicate;
|
||||||
}
|
}
|
||||||
@ -153,7 +154,7 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
public JSONWriter beginObj() {
|
public JSONWriter beginObj() {
|
||||||
append(CharUtil.DELIM_START);
|
append(CharUtil.DELIM_START);
|
||||||
needSeparator = false;
|
needSeparator = false;
|
||||||
indent += indentFactor;
|
level += 1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +176,7 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
public JSONWriter beginArray() {
|
public JSONWriter beginArray() {
|
||||||
append(CharUtil.BRACKET_START);
|
append(CharUtil.BRACKET_START);
|
||||||
needSeparator = false;
|
needSeparator = false;
|
||||||
indent += indentFactor;
|
level += 1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
append(CharUtil.COMMA);
|
append(CharUtil.COMMA);
|
||||||
}
|
}
|
||||||
// 换行缩进
|
// 换行缩进
|
||||||
writeLF().writeSpace(indent);
|
writeNewLine().writeIndent(level);
|
||||||
writeQuoteStrValue(key);
|
writeQuoteStrValue(key);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -271,15 +272,28 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 写出空格
|
* 写出空白字符,默认写出一个空格
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JSONWriter writeSpaceAfterSeparators() {
|
||||||
|
if (this.formatStyle.isSpaceAfterSeparators()) {
|
||||||
|
return append(CharUtil.SPACE);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出缩进
|
||||||
*
|
*
|
||||||
* @param count 空格数
|
* @param count 空格数
|
||||||
*/
|
*/
|
||||||
public void writeSpace(final int count) {
|
public void writeIndent(final int count) {
|
||||||
if (indentFactor > 0) {
|
final String indentStr = this.formatStyle.getIndent();
|
||||||
|
if (StrUtil.isNotEmpty(indentStr)) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
//noinspection resource
|
//noinspection resource
|
||||||
append(CharUtil.SPACE);
|
append(indentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,10 +303,11 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public JSONWriter writeLF() {
|
public JSONWriter writeNewLine() {
|
||||||
if (indentFactor > 0) {
|
final String newline = this.formatStyle.getNewline();
|
||||||
|
if (StrUtil.isNotEmpty(newline)) {
|
||||||
//noinspection resource
|
//noinspection resource
|
||||||
append(CharUtil.LF);
|
append(newline);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -326,16 +341,16 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
final String numberStr = NumberUtil.toStr(number, isStripTrailingZeros);
|
final String numberStr = NumberUtil.toStr(number, isStripTrailingZeros);
|
||||||
|
|
||||||
// 检查有效性
|
// 检查有效性
|
||||||
if(!ReUtil.isMatch(JSON_NUMBER_PATTERN, numberStr)){
|
if (!ReUtil.isMatch(JSON_NUMBER_PATTERN, numberStr)) {
|
||||||
throw new JSONException("Invalid RFC8259 JSON format number: " + numberStr);
|
throw new JSONException("Invalid RFC8259 JSON format number: " + numberStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
final NumberWriteMode numberWriteMode = (null == config) ? NumberWriteMode.NORMAL : config.getNumberWriteMode();
|
final NumberWriteMode numberWriteMode = (null == config) ? NumberWriteMode.NORMAL : config.getNumberWriteMode();
|
||||||
switch (numberWriteMode){
|
switch (numberWriteMode) {
|
||||||
case JS:
|
case JS:
|
||||||
if(number.longValue() > JS_MAX_NUMBER){
|
if (number.longValue() > JS_MAX_NUMBER) {
|
||||||
writeQuoteStrValue(numberStr);
|
writeQuoteStrValue(numberStr);
|
||||||
} else{
|
} else {
|
||||||
return writeRaw(numberStr);
|
return writeRaw(numberStr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -394,9 +409,9 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
private JSONWriter end(final boolean arrayMode) {
|
private JSONWriter end(final boolean arrayMode) {
|
||||||
// 结束子缩进
|
// 结束子缩进
|
||||||
indent -= indentFactor;
|
level -= 1;
|
||||||
// 换行缩进
|
// 换行缩进
|
||||||
writeLF().writeSpace(indent);
|
writeNewLine().writeIndent(level);
|
||||||
append(arrayMode ? CharUtil.BRACKET_END : CharUtil.DELIM_END);
|
append(arrayMode ? CharUtil.BRACKET_END : CharUtil.DELIM_END);
|
||||||
flush();
|
flush();
|
||||||
// 当前对象或数组结束,当新的
|
// 当前对象或数组结束,当新的
|
||||||
@ -419,10 +434,10 @@ public class JSONWriter implements Appendable, Flushable, Closeable {
|
|||||||
}
|
}
|
||||||
// 换行缩进
|
// 换行缩进
|
||||||
//noinspection resource
|
//noinspection resource
|
||||||
writeLF().writeSpace(indent);
|
writeNewLine().writeIndent(level);
|
||||||
} else {
|
} else {
|
||||||
//noinspection resource
|
//noinspection resource
|
||||||
append(CharUtil.COLON).writeSpace(1);
|
append(CharUtil.COLON).writeSpaceAfterSeparators();
|
||||||
}
|
}
|
||||||
needSeparator = true;
|
needSeparator = true;
|
||||||
return writeObjValue(value);
|
return writeObjValue(value);
|
||||||
|
@ -18,6 +18,7 @@ package org.dromara.hutool.json;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.dromara.hutool.core.collection.ListUtil;
|
import org.dromara.hutool.core.collection.ListUtil;
|
||||||
|
import org.dromara.hutool.core.date.DateTime;
|
||||||
import org.dromara.hutool.core.date.DateUtil;
|
import org.dromara.hutool.core.date.DateUtil;
|
||||||
import org.dromara.hutool.core.map.MapUtil;
|
import org.dromara.hutool.core.map.MapUtil;
|
||||||
import org.dromara.hutool.json.test.bean.Price;
|
import org.dromara.hutool.json.test.bean.Price;
|
||||||
@ -144,14 +145,16 @@ public class JSONUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void toJsonStrTest() {
|
public void toJsonPrettyStrTest() {
|
||||||
|
final DateTime date = DateUtil.date(1727800929605L);
|
||||||
final UserA a1 = new UserA();
|
final UserA a1 = new UserA();
|
||||||
a1.setA("aaaa");
|
a1.setA("aaaa");
|
||||||
a1.setDate(DateUtil.now());
|
a1.setDate(date);
|
||||||
a1.setName("AAAAName");
|
a1.setName("AAAAName");
|
||||||
|
|
||||||
final UserA a2 = new UserA();
|
final UserA a2 = new UserA();
|
||||||
a2.setA("aaaa222");
|
a2.setA("aaaa222");
|
||||||
a2.setDate(DateUtil.now());
|
a2.setDate(date);
|
||||||
a2.setName("AAAA222Name");
|
a2.setName("AAAA222Name");
|
||||||
|
|
||||||
final ArrayList<UserA> list = ListUtil.of(a1, a2);
|
final ArrayList<UserA> list = ListUtil.of(a1, a2);
|
||||||
@ -159,9 +162,22 @@ public class JSONUtilTest {
|
|||||||
map.put("total", 13);
|
map.put("total", 13);
|
||||||
map.put("rows", list);
|
map.put("rows", list);
|
||||||
|
|
||||||
final String str = JSONUtil.toJsonPrettyStr(map);
|
final String jsonStr = JSONUtil.toJsonPrettyStr(map);
|
||||||
JSONUtil.parse(str);
|
Assertions.assertEquals("{\n" +
|
||||||
Assertions.assertNotNull(str);
|
" \"total\": 13,\n" +
|
||||||
|
" \"rows\": [\n" +
|
||||||
|
" {\n" +
|
||||||
|
" \"name\": \"AAAAName\",\n" +
|
||||||
|
" \"a\": \"aaaa\",\n" +
|
||||||
|
" \"date\": 1727800929605\n" +
|
||||||
|
" },\n" +
|
||||||
|
" {\n" +
|
||||||
|
" \"name\": \"AAAA222Name\",\n" +
|
||||||
|
" \"a\": \"aaaa222\",\n" +
|
||||||
|
" \"date\": 1727800929605\n" +
|
||||||
|
" }\n" +
|
||||||
|
" ]\n" +
|
||||||
|
"}", jsonStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -16,12 +16,16 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.json.engine;
|
package org.dromara.hutool.json.engine;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.annotation.JSONField;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.hutool.core.date.DateTime;
|
import org.dromara.hutool.core.date.DateTime;
|
||||||
import org.dromara.hutool.core.date.DateUtil;
|
import org.dromara.hutool.core.date.DateUtil;
|
||||||
import org.dromara.hutool.core.date.TimeUtil;
|
import org.dromara.hutool.core.date.TimeUtil;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
@ -56,6 +60,11 @@ public class JSONEngineTest {
|
|||||||
Arrays.stream(engineNames).forEach(this::assertEmptyBeanToJson);
|
Arrays.stream(engineNames).forEach(this::assertEmptyBeanToJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toStringOrFromStringTest() {
|
||||||
|
Arrays.stream(engineNames).forEach(this::assertToStringOrFromString);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertWriteDateFormat(final String engineName) {
|
private void assertWriteDateFormat(final String engineName) {
|
||||||
final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
|
final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
|
||||||
final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
|
final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
|
||||||
@ -110,8 +119,37 @@ public class JSONEngineTest {
|
|||||||
assertEquals("{}", jsonString);
|
assertEquals("{}", jsonString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertToStringOrFromString(final String engineName) {
|
||||||
|
final JSONEngine engine = JSONEngineFactory.createEngine(engineName);
|
||||||
|
final TestBean testBean = new TestBean("张三", 18, true);
|
||||||
|
|
||||||
|
final String jsonStr = "{\"name\":\"张三\",\"age\":18,\"gender\":true}";
|
||||||
|
if("moshi".equals(engineName)){
|
||||||
|
// TODO Moshi无法指定key的顺序
|
||||||
|
assertEquals("{\"age\":18,\"gender\":true,\"name\":\"张三\"}", engine.toJsonString(testBean));
|
||||||
|
}else{
|
||||||
|
assertEquals(jsonStr, engine.toJsonString(testBean));
|
||||||
|
}
|
||||||
|
|
||||||
|
final TestBean testBean1 = engine.deserialize(new StringReader(jsonStr), TestBean.class);
|
||||||
|
assertEquals(testBean, testBean1);
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
private static class EmptyBean{
|
private static class EmptyBean{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class TestBean {
|
||||||
|
// 解决输出顺序问题
|
||||||
|
@JSONField(ordinal = 1)
|
||||||
|
private String name;
|
||||||
|
@JSONField(ordinal = 2)
|
||||||
|
private int age;
|
||||||
|
@JSONField(ordinal = 3)
|
||||||
|
private boolean gender;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user