From 3104b89e73a2441ea0e9f202af800490a8d5bb5e Mon Sep 17 00:00:00 2001 From: canonical Date: Sun, 3 Sep 2023 12:31:34 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E6=88=90=E4=B8=9A=E5=8A=A1=E8=A7=84?= =?UTF-8?q?=E5=88=99=E8=BE=93=E5=85=A5=E6=95=B0=E6=8D=AE=E6=89=80=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84json=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/NopRuleDefinitionBizModel.java | 13 ++ .../xmeta/jsonschema/XSchemaToJsonSchema.java | 170 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 nop-xlang/src/main/java/io/nop/xlang/xmeta/jsonschema/XSchemaToJsonSchema.java diff --git a/nop-rule/nop-rule-service/src/main/java/io/nop/rule/service/entity/NopRuleDefinitionBizModel.java b/nop-rule/nop-rule-service/src/main/java/io/nop/rule/service/entity/NopRuleDefinitionBizModel.java index 6d3a80624..11a5bb95d 100644 --- a/nop-rule/nop-rule-service/src/main/java/io/nop/rule/service/entity/NopRuleDefinitionBizModel.java +++ b/nop-rule/nop-rule-service/src/main/java/io/nop/rule/service/entity/NopRuleDefinitionBizModel.java @@ -4,9 +4,11 @@ import io.nop.api.core.annotations.biz.BizAction; import io.nop.api.core.annotations.biz.BizModel; import io.nop.api.core.annotations.biz.BizQuery; +import io.nop.api.core.annotations.biz.RequestBean; import io.nop.api.core.annotations.core.Name; import io.nop.api.core.beans.DictBean; import io.nop.api.core.beans.DictOptionBean; +import io.nop.api.core.beans.WebContentBean; import io.nop.biz.crud.CrudBizModel; import io.nop.biz.crud.EntityData; import io.nop.commons.util.StringHelper; @@ -15,6 +17,7 @@ import io.nop.file.core.FileConstants; import io.nop.orm.IOrmEntityFileStore; import io.nop.orm.OrmConstants; +import io.nop.rule.api.beans.RuleKeyBean; import io.nop.rule.core.excel.RuleExcelModelParser; import io.nop.rule.core.model.RuleModel; import io.nop.rule.dao.entity.NopRuleDefinition; @@ -22,6 +25,8 @@ import io.nop.rule.dao.model.DaoRuleModelSaver; import io.nop.rule.service.NopRuleConstants; import io.nop.web.page.condition.ConditionSchemaHelper; +import io.nop.xlang.xmeta.ISchema; +import io.nop.xlang.xmeta.jsonschema.XSchemaToJsonSchema; import javax.inject.Inject; import java.util.List; @@ -71,6 +76,14 @@ public DictBean getOutputFields(@Name(NopRuleConstants.RULE_ID_NAME) String rule return dict; } + @BizQuery + public WebContentBean getInputJsonSchema(@RequestBean RuleKeyBean ruleKey, IServiceContext context) { + NopRuleDefinition rule = ruleModelLoader.loadRuleDefinition(ruleKey.getRuleName(), ruleKey.getRuleVersion()); + ISchema schema = ruleModelLoader.buildRuleInputSchema(rule); + Map jsonSchema = XSchemaToJsonSchema.instance().toJsonSchema(schema, context); + return WebContentBean.json(jsonSchema); + } + @Override @BizAction protected void defaultPrepareSave(EntityData entityData, IServiceContext context) { diff --git a/nop-xlang/src/main/java/io/nop/xlang/xmeta/jsonschema/XSchemaToJsonSchema.java b/nop-xlang/src/main/java/io/nop/xlang/xmeta/jsonschema/XSchemaToJsonSchema.java new file mode 100644 index 000000000..562890730 --- /dev/null +++ b/nop-xlang/src/main/java/io/nop/xlang/xmeta/jsonschema/XSchemaToJsonSchema.java @@ -0,0 +1,170 @@ +package io.nop.xlang.xmeta.jsonschema; + +import io.nop.api.core.beans.DictBean; +import io.nop.api.core.context.ContextProvider; +import io.nop.commons.type.StdDataType; +import io.nop.core.context.IServiceContext; +import io.nop.core.dict.DictProvider; +import io.nop.xlang.xmeta.IObjPropMeta; +import io.nop.xlang.xmeta.IObjSchema; +import io.nop.xlang.xmeta.ISchema; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class XSchemaToJsonSchema { + static XSchemaToJsonSchema _instance = new XSchemaToJsonSchema(); + + public static void registerInstance(XSchemaToJsonSchema instance) { + _instance = instance; + } + + public static XSchemaToJsonSchema instance() { + return _instance; + } + + public Map toJsonSchema(ISchema schema, IServiceContext context) { + Map ret = new LinkedHashMap<>(); + if (schema == null) { + return ret; + } + + if (schema.isObjSchema()) { + toObjectSchema(ret, schema, context); + } else if (schema.isListSchema()) { + ret.put("type", "array"); + addArraySpecificProps(ret, schema); + ret.put("items", toJsonSchema(schema.getItemSchema(), context)); + } else if (schema.isUnionSchema()) { + List schemas = schema.getOneOf(); + if (schemas != null) { + List> list = new ArrayList<>(schemas.size()); + for (ISchema subSchema : schemas) { + list.add(toJsonSchema(subSchema, context)); + } + ret.put("anyOf", schemas); + } + } else { + toSimpleSchema(ret, schema, context); + } + return ret; + } + + void addArraySpecificProps(Map ret, ISchema schema) { + if (schema.getMinItems() != null) + ret.put("minItems", schema.getMinItems()); + if (schema.getMaxItems() != null) { + ret.put("maxItems", schema.getMaxItems()); + } + } + + void addObjectSpecificProps(Map ret, IObjSchema schema) { + if (schema.getMinProperties() != null) { + ret.put("minProperties", schema.getMinProperties()); + } + if (schema.getMaxProperties() != null) { + ret.put("maxProperties", schema.getMaxProperties()); + } + } + + void toObjectSchema(Map ret, IObjSchema schema, IServiceContext context) { + ret.put("type", "object"); + addObjectSpecificProps(ret, schema); + + List required = new ArrayList<>(); + List props = schema.getProps(); + Map propSchemas = new LinkedHashMap<>(); + if (props != null) { + props.forEach(prop -> { + if (prop.isMandatory()) + required.add(prop.getName()); + + propSchemas.put(prop.getName(), toJsonSchema(prop.getSchema(), context)); + }); + } + ret.put("properties", propSchemas); + if (!required.isEmpty()) + ret.put("required", required); + } + + void toSimpleSchema(Map ret, ISchema schema, IServiceContext context) { + if (schema == null) + return; + + StdDataType dataType = schema.getStdDataType(); + if (dataType == null) + dataType = StdDataType.STRING; + + if (dataType == StdDataType.MAP) { + toMapSchema(ret); + } else { + String jsonType = dataType.getJsonType(); + ret.put("type", jsonType); + if (schema.getMax() != null) { + ret.put("maximum", schema.getMax()); + } + if (schema.getMin() != null) { + ret.put("minimum", schema.getMin()); + } + + if (schema.getMinLength() != null) + ret.put("minLength", schema.getMinLength()); + if (schema.getMaxLength() != null) + ret.put("maxLength", schema.getMaxLength()); + if (schema.getPattern() != null) + ret.put("pattern", schema.getPattern()); + + String format = getFormat(schema); + if (format != null) + ret.put("format", format); + + String dict = schema.getDict(); + if (dict != null) { + String locale = ContextProvider.currentLocale(); + DictBean dictBean = DictProvider.instance().getDict(locale, dict, context.getCache(), context); + if (dictBean != null) { + ret.put("enum", dictBean.getValues()); + } + } + } + } + + protected String getFormat(ISchema schema) { + StdDataType dataType = schema.getStdDataType(); + if (dataType == StdDataType.DATETIME) + return "date-time"; + if (dataType == StdDataType.TIME) + return "time"; + if (dataType == StdDataType.DATE) + return "date"; + + if (dataType == StdDataType.DURATION) + return "duration"; + + String domain = schema.getStdDomain(); + if (domain == null) + return null; + switch (domain) { + case "email": + return "email"; + case "url": + return "url"; + case "ipv4": + return "ipv4"; + case "ipv6": + return "ipv6"; + } + return null; + } + + void toMapSchema(Map ret) { + ret.put("type", "object"); + ret.put("additionalProperties", true); + Map pattern = new HashMap<>(); + pattern.put(".*", new HashMap<>()); + ret.put("patternProperties", pattern); + } +}