mirror of https://github.com/halo-dev/halo
Complete yaml conversion test
parent
8a8d55eae1
commit
8054b8f714
|
@ -1,5 +1,8 @@
|
||||||
package run.halo.app.model.enums;
|
package run.halo.app.model.enums;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data type enum.
|
* Data type enum.
|
||||||
*
|
*
|
||||||
|
@ -27,4 +30,22 @@ public enum DataType implements ValueEnum<Integer> {
|
||||||
public Integer getValue() {
|
public Integer getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data type of string.
|
||||||
|
*
|
||||||
|
* @param type data type string
|
||||||
|
* @return corresponding data type, default is `STRING` if the type is missing
|
||||||
|
*/
|
||||||
|
public static DataType typeOf(@Nullable Object type) {
|
||||||
|
if (type != null) {
|
||||||
|
for (DataType datatype : values()) {
|
||||||
|
if (datatype.name().equalsIgnoreCase(type.toString())) {
|
||||||
|
return datatype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package run.halo.app.model.enums;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input type enum.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 4/10/19
|
||||||
|
*/
|
||||||
|
public enum InputType {
|
||||||
|
|
||||||
|
TEXT,
|
||||||
|
|
||||||
|
NUMBER,
|
||||||
|
|
||||||
|
RADIO,
|
||||||
|
|
||||||
|
SELECT,
|
||||||
|
|
||||||
|
TEXTAREA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert type to input type.
|
||||||
|
*
|
||||||
|
* @param type input type
|
||||||
|
* @return corresponding input type or TEXT if the given type is missing or mismatch
|
||||||
|
*/
|
||||||
|
public static InputType typeOf(@Nullable Object type) {
|
||||||
|
if (type != null) {
|
||||||
|
for (InputType inputType : values()) {
|
||||||
|
if (inputType.name().equalsIgnoreCase(type.toString())) {
|
||||||
|
return inputType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ public interface ThemeSettingService {
|
||||||
* @param themeId theme id must not be blank
|
* @param themeId theme id must not be blank
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
void save(@Nullable Map<String, String> settings, @NonNull String themeId);
|
void save(@Nullable Map<String, Object> settings, @NonNull String themeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists theme settings by theme id.
|
* Lists theme settings by theme id.
|
||||||
|
@ -54,5 +54,5 @@ public interface ThemeSettingService {
|
||||||
* @return theme setting map
|
* @return theme setting map
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
Map<String, String> listAsMapBy(@NonNull String themeId);
|
Map<String, Object> listAsMapBy(@NonNull String themeId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Map<String, String> settings, String themeId) {
|
public void save(Map<String, Object> settings, String themeId) {
|
||||||
assertThemeIdHasText(themeId);
|
assertThemeIdHasText(themeId);
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(settings)) {
|
if (CollectionUtils.isEmpty(settings)) {
|
||||||
|
@ -70,7 +70,7 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the settings
|
// Save the settings
|
||||||
settings.forEach((key, value) -> save(key, value, themeId));
|
settings.forEach((key, value) -> save(key, value.toString(), themeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -81,7 +81,9 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> listAsMapBy(String themeId) {
|
public Map<String, Object> listAsMapBy(String themeId) {
|
||||||
|
|
||||||
|
// TODO Convert to corresponding data type
|
||||||
return ServiceUtils.convertToMap(listBy(themeId), ThemeSetting::getKey, ThemeSetting::getValue);
|
return ServiceUtils.convertToMap(listBy(themeId), ThemeSetting::getKey, ThemeSetting::getValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,13 +85,13 @@ public class ThemeController {
|
||||||
|
|
||||||
@GetMapping("activation/settings")
|
@GetMapping("activation/settings")
|
||||||
@ApiOperation("Lists activated theme settings")
|
@ApiOperation("Lists activated theme settings")
|
||||||
public Map<String, String> listSettingsBy() {
|
public Map<String, Object> listSettingsBy() {
|
||||||
return themeSettingService.listAsMapBy(themeService.getActivatedThemeId());
|
return themeSettingService.listAsMapBy(themeService.getActivatedThemeId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("activation/settings")
|
@PostMapping("activation/settings")
|
||||||
@ApiOperation("Saves theme settings")
|
@ApiOperation("Saves theme settings")
|
||||||
public void saveSettingsBy(@RequestBody Map<String, String> settings) {
|
public void saveSettingsBy(@RequestBody Map<String, Object> settings) {
|
||||||
themeSettingService.save(settings, themeService.getActivatedThemeId());
|
themeSettingService.save(settings, themeService.getActivatedThemeId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ public class ThemeController {
|
||||||
@PostMapping("{themeId}/settings")
|
@PostMapping("{themeId}/settings")
|
||||||
@ApiOperation("Saves theme settings")
|
@ApiOperation("Saves theme settings")
|
||||||
public void saveSettingsBy(@PathVariable("themeId") String themeId,
|
public void saveSettingsBy(@PathVariable("themeId") String themeId,
|
||||||
@RequestBody Map<String, String> settings) {
|
@RequestBody Map<String, Object> settings) {
|
||||||
themeSettingService.save(settings, themeId);
|
themeSettingService.save(settings, themeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,14 @@ package run.halo.app.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||||
|
import lombok.Data;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import run.halo.app.model.enums.DataType;
|
||||||
|
import run.halo.app.model.enums.InputType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yaml test.
|
* Yaml test.
|
||||||
|
@ -21,26 +26,358 @@ public class YamlTest {
|
||||||
@Test
|
@Test
|
||||||
public void readYamlTest() throws IOException {
|
public void readYamlTest() throws IOException {
|
||||||
|
|
||||||
String yaml = "style:\n" +
|
String yaml = "sns:\n" +
|
||||||
" name: Style settings\n" +
|
" label: 社交资料设置\n" +
|
||||||
" items:\n" +
|
" items:\n" +
|
||||||
" post_title_lower:\n" +
|
" rss:\n" +
|
||||||
" name: post_title_lower\n" +
|
" name: rss\n" +
|
||||||
" description: Post title lower\n" +
|
" label: RSS\n" +
|
||||||
" type: radio\n" +
|
" type: radio\n" +
|
||||||
" defaultValue: true\n" +
|
" default: true\n" +
|
||||||
" options:\n" +
|
" options:\n" +
|
||||||
" - value: true\n" +
|
" - value: true\n" +
|
||||||
" label: Enabled\n" +
|
" label: 开启\n" +
|
||||||
" - value: false\n" +
|
" - value: false\n" +
|
||||||
" label: Disabled\n" +
|
" label: 关闭\n" +
|
||||||
" custom_style:\n" +
|
" twitter:\n" +
|
||||||
" name: custom_style\n" +
|
" name: twitter\n" +
|
||||||
" description: Custom style\n" +
|
" label: Twitter\n" +
|
||||||
" type: textarea\n";
|
" type: text\n" +
|
||||||
|
" facebook:\n" +
|
||||||
|
" name: facebook\n" +
|
||||||
|
" label: Facebook\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
"style:\n" +
|
||||||
|
" label: 样式设置\n" +
|
||||||
|
" items:\n" +
|
||||||
|
" icon:\n" +
|
||||||
|
" name: icon\n" +
|
||||||
|
" label: 右上角图标\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" post_title_uppper:\n" +
|
||||||
|
" name: post_title_uppper\n" +
|
||||||
|
" label: 文章标题大写\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭";
|
||||||
|
|
||||||
Object config = yamlMapper.readValue(yaml, Object.class);
|
LinkedHashMap config = yamlMapper.readValue(yaml, LinkedHashMap.class);
|
||||||
|
|
||||||
|
System.out.println(config.getClass());
|
||||||
|
|
||||||
System.out.println(jsonMapper.writeValueAsString(config));
|
System.out.println(jsonMapper.writeValueAsString(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void readAnotherYamlTest() throws IOException {
|
||||||
|
String yaml = "sns:\n" +
|
||||||
|
" label: 社交资料设置\n" +
|
||||||
|
" items:\n" +
|
||||||
|
" - name: rss\n" +
|
||||||
|
" label: RSS\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭\n" +
|
||||||
|
" - name: twitter\n" +
|
||||||
|
" label: Twitter\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: facebook\n" +
|
||||||
|
" label: Facebook\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: instagram\n" +
|
||||||
|
" label: Instagram\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
"style:\n" +
|
||||||
|
" label: 样式设置\n" +
|
||||||
|
" items:\n" +
|
||||||
|
" - name: icon\n" +
|
||||||
|
" label: 右上角图标\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: post_title_uppper\n" +
|
||||||
|
" label: 文章标题大写\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭\n" +
|
||||||
|
" - name: blog_title_uppper\n" +
|
||||||
|
" label: 博客标题大写\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭\n";
|
||||||
|
|
||||||
|
Object config = yamlMapper.readValue(yaml, Object.class);
|
||||||
|
|
||||||
|
List<Tab> tabs = handleTabs(config);
|
||||||
|
|
||||||
|
System.out.println(config.getClass());
|
||||||
|
|
||||||
|
System.out.println(tabs);
|
||||||
|
|
||||||
|
System.out.println(jsonMapper.writeValueAsString(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertYamlTest() throws IOException {
|
||||||
|
String yaml = "- name: sns\n" +
|
||||||
|
" label: 社交资料设置\n" +
|
||||||
|
" items:\n" +
|
||||||
|
" - name: rss\n" +
|
||||||
|
" label: RSS\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭\n" +
|
||||||
|
" - name: twitter\n" +
|
||||||
|
" label: Twitter\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: facebook\n" +
|
||||||
|
" label: Facebook\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: instagram\n" +
|
||||||
|
" label: Instagram\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
"- name: style\n" +
|
||||||
|
" label: 样式设置\n" +
|
||||||
|
" items:\n" +
|
||||||
|
" - name: icon\n" +
|
||||||
|
" label: 右上角图标\n" +
|
||||||
|
" type: text\n" +
|
||||||
|
" - name: post_title_uppper\n" +
|
||||||
|
" label: 文章标题大写\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭\n" +
|
||||||
|
" - name: blog_title_uppper\n" +
|
||||||
|
" label: 博客标题大写\n" +
|
||||||
|
" type: radio\n" +
|
||||||
|
" default: true\n" +
|
||||||
|
" options:\n" +
|
||||||
|
" - value: true\n" +
|
||||||
|
" label: 开启\n" +
|
||||||
|
" - value: false\n" +
|
||||||
|
" label: 关闭";
|
||||||
|
|
||||||
|
|
||||||
|
Object config = yamlMapper.readValue(yaml, Object.class);
|
||||||
|
|
||||||
|
List<Tab> tabs = handleTabs(config);
|
||||||
|
|
||||||
|
System.out.println(config.getClass());
|
||||||
|
|
||||||
|
System.out.print(tabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Tab> handleTabs(@Nullable Object config) {
|
||||||
|
List<Tab> tabs = new LinkedList<>();
|
||||||
|
|
||||||
|
if (config instanceof List) {
|
||||||
|
List configList = (List) config;
|
||||||
|
|
||||||
|
// Resolve tab
|
||||||
|
|
||||||
|
configList.forEach(tabYaml -> {
|
||||||
|
// tabYaml should be map
|
||||||
|
if (!(tabYaml instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map tabMap = ((Map) tabYaml);
|
||||||
|
|
||||||
|
Tab tab = new Tab();
|
||||||
|
|
||||||
|
tab.setName(tabMap.get("name").toString());
|
||||||
|
tab.setLabel(tabMap.get("label").toString());
|
||||||
|
|
||||||
|
// Handle items
|
||||||
|
tab.setItems(handleItems(tabMap.get("items")));
|
||||||
|
|
||||||
|
tabs.add(tab);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (config instanceof Map) {
|
||||||
|
Map configMap = (Map) config;
|
||||||
|
configMap.forEach((key, value) -> {
|
||||||
|
// key: tab name
|
||||||
|
// value: tab property, should be a map
|
||||||
|
if (!(value instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map tabMap = (Map) value;
|
||||||
|
|
||||||
|
Tab tab = new Tab();
|
||||||
|
|
||||||
|
tab.setName(key.toString());
|
||||||
|
tab.setLabel(tabMap.get("label").toString());
|
||||||
|
|
||||||
|
// Handle items
|
||||||
|
tab.setItems(handleItems(tabMap.get("items")));
|
||||||
|
|
||||||
|
tabs.add(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Item> handleItems(@Nullable Object items) {
|
||||||
|
|
||||||
|
if (items == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Item> result = new LinkedList<>();
|
||||||
|
|
||||||
|
if (items instanceof List) {
|
||||||
|
((List) items).forEach(itemYaml -> {
|
||||||
|
if (!(itemYaml instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be Map
|
||||||
|
Map itemMap = (Map) itemYaml;
|
||||||
|
|
||||||
|
// Build item
|
||||||
|
Item item = new Item();
|
||||||
|
|
||||||
|
item.setName(itemMap.get("name").toString());
|
||||||
|
item.setLabel(itemMap.getOrDefault("label", item.getName()).toString());
|
||||||
|
item.setDataType(DataType.typeOf(itemMap.get("data_type")));
|
||||||
|
item.setType(InputType.typeOf(itemMap.get("type")));
|
||||||
|
item.setDefaultValue(itemMap.get("default"));
|
||||||
|
|
||||||
|
// Handle options
|
||||||
|
item.setOptions(handleOptions(itemMap.get("options")));
|
||||||
|
|
||||||
|
// Add item
|
||||||
|
result.add(item);
|
||||||
|
});
|
||||||
|
} else if (items instanceof Map) {
|
||||||
|
Map itemsMap = (Map) items;
|
||||||
|
|
||||||
|
itemsMap.forEach((key, value) -> {
|
||||||
|
if (!(value instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map itemMap = (Map) value;
|
||||||
|
|
||||||
|
// key: item name
|
||||||
|
Item item = new Item();
|
||||||
|
item.setName(key.toString());
|
||||||
|
item.setLabel(itemMap.getOrDefault("label", item.getName()).toString());
|
||||||
|
item.setDataType(DataType.typeOf(itemMap.get("data_type")));
|
||||||
|
item.setType(InputType.typeOf(itemMap.get("type")));
|
||||||
|
item.setDefaultValue(itemMap.get("default"));
|
||||||
|
|
||||||
|
// Handle options
|
||||||
|
item.setOptions(handleOptions(itemMap.get("options")));
|
||||||
|
|
||||||
|
// Add item
|
||||||
|
result.add(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Option> handleOptions(@Nullable Object options) {
|
||||||
|
|
||||||
|
if (options == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Option> result = new LinkedList<>();
|
||||||
|
|
||||||
|
if (options instanceof List) {
|
||||||
|
((List) options).forEach(optionYaml -> {
|
||||||
|
// optionYaml should be Map
|
||||||
|
if (!(optionYaml instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map optionMap = (Map) optionYaml;
|
||||||
|
|
||||||
|
// Build option
|
||||||
|
Option option = new Option();
|
||||||
|
// TODO Convert the value type
|
||||||
|
option.setValue(optionMap.get("value"));
|
||||||
|
option.setLabel(optionMap.get("label").toString());
|
||||||
|
|
||||||
|
result.add(option);
|
||||||
|
});
|
||||||
|
} else if (options instanceof Map) {
|
||||||
|
Map optionsMap = (Map) options;
|
||||||
|
optionsMap.forEach((key, value) -> {
|
||||||
|
// key: option value
|
||||||
|
// value: Map that contains option label
|
||||||
|
|
||||||
|
if (!(value instanceof Map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map optionMap = (Map) value;
|
||||||
|
|
||||||
|
|
||||||
|
Option option = new Option();
|
||||||
|
// TODO Convert the value type
|
||||||
|
option.setValue(key);
|
||||||
|
option.setLabel(optionMap.get("label").toString());
|
||||||
|
|
||||||
|
result.add(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
private class Tab {
|
||||||
|
private String name;
|
||||||
|
private String label;
|
||||||
|
private List<Item> items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
private class Item {
|
||||||
|
private String name;
|
||||||
|
private String label;
|
||||||
|
private InputType type;
|
||||||
|
private DataType dataType;
|
||||||
|
private Object defaultValue;
|
||||||
|
|
||||||
|
private List<Option> options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
private class Option {
|
||||||
|
private String label;
|
||||||
|
private Object value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue