diff --git a/APIJSONORM/README.md b/APIJSONORM/README.md
index 0cb431e2..745733b4 100644
--- a/APIJSONORM/README.md
+++ b/APIJSONORM/README.md
@@ -21,7 +21,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
com.github.Tencent
APIJSON
- LATEST
+ 8.1.8
```
@@ -45,7 +45,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
#### 2. Add the APIJSON dependency in one of your modules(such as `app`)
```gradle
dependencies {
- implementation 'com.github.Tencent:APIJSON:latest'
+ implementation 'com.github.Tencent:APIJSON:8.1.8'
}
```
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 8b36d0da..7a35eb01 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 8.1.3
+ 8.1.8
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index d301cdf0..f00e495a 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -12,6 +12,7 @@
*/
public class Log {
public static boolean DEBUG = false;
+ public static final String VERSION = "8.1.8";
public static final String LEVEL_VERBOSE = "VERBOSE";
public static final String LEVEL_INFO = "INFO";
@@ -21,7 +22,6 @@ public class Log {
public static String LEVEL = LEVEL_WARN;
- public static final String VERSION = "8.1.5";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
index 42831775..7ed7fc4d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
@@ -31,9 +31,11 @@ public abstract class AbstractFunctionParser, L
/**开启支持远程函数
*/
public static boolean ENABLE_REMOTE_FUNCTION = true;
- /**开启支持远程函数中的 JavaScript 脚本形式
+ /**开启支持远程函数中的 JavaScript/Python/Lua/PHP 等脚本形式。
+ * JDK 8~13 可用自带 Nashorn 这个 js 引擎,注意配置 ClassFilter 防脚本注入攻击;
+ * 其它语言及 JDK 14+ 都必须依赖外部脚本引擎,注意按对应引擎说明方式防脚本注入攻击,最好是沙箱环境。
*/
- public static boolean ENABLE_SCRIPT_FUNCTION = true;
+ public static boolean ENABLE_SCRIPT_FUNCTION = false;
//
// >
@@ -961,4 +963,4 @@ public V getArgVal(String key, Class clazz, boolean defaultValue) throws
}
}
-}
\ No newline at end of file
+}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index c49c9cd2..5fd00c17 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -375,7 +375,7 @@ else if (_method == PUT && value instanceof List> && (whereList == null || whe
if (isSubquery == false) { // 解决 SQL 语法报错,子查询不能 EXPLAIN
Boolean exp = parser.getGlobalExplain();
- if (sch != null) {
+ if (exp != null) {
sqlRequest.putIfAbsent(JSONMap.KEY_EXPLAIN, exp);
}
@@ -720,7 +720,7 @@ public void onPUTArrayParse(@NotNull String key, @NotNull L array) throws Except
if (apijson.JSON.isBoolOrNumOrStr(target)) {
throw new NullPointerException("PUT " + path + ", " + realKey + " 类型为 " + target.getClass().getSimpleName() + ","
+ "不支持 Boolean, String, Number 等类型字段使用 'key+': [] 或 'key-': [] !"
- + "对应字段在数据库的值必须为 L, JSONRequest 中的一种!"
+ + "对应字段在数据库的值必须为 JSONArray, JSONObject 中的一种!"
+ "值为 JSONRequest 类型时传参必须是 'key+': [{'key': value, 'key2': value2}] 或 'key-': ['key', 'key2'] !"
);
}
@@ -734,7 +734,7 @@ public void onPUTArrayParse(@NotNull String key, @NotNull L array) throws Except
if (isAdd == false) {
throw new NullPointerException("PUT " + path + ", " + realKey + (target == null ? " 值为 null,不支持移除!"
: " 类型为 " + target.getClass().getSimpleName() + ",不支持这样移除!")
- + "对应字段在数据库的值必须为 L, JSONRequest 中的一种,且 key- 移除时,本身的值不能为 null!"
+ + "对应字段在数据库的值必须为 JSONArray, JSONObject 中的一种,且 key- 移除时,本身的值不能为 null!"
+ "值为 JSONRequest 类型时传参必须是 'key+': [{'key': value, 'key2': value2}] 或 'key-': ['key', 'key2'] !"
);
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index e2403122..defccc93 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -527,6 +527,28 @@ public M parseResponse(M request) {
return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
}
+ try {
+ setGlobalDatabase(getString(requestObject, KEY_DATABASE));
+ setGlobalDatasource(getString(requestObject, KEY_DATASOURCE));
+ setGlobalNamespace(getString(requestObject, KEY_NAMESPACE));
+ setGlobalCatalog(getString(requestObject, KEY_CATALOG));
+ setGlobalSchema(getString(requestObject, KEY_SCHEMA));
+
+ setGlobalExplain(getBoolean(requestObject, KEY_EXPLAIN));
+ setGlobalCache(getString(requestObject, KEY_CACHE));
+
+ requestObject.remove(KEY_DATABASE);
+ requestObject.remove(KEY_DATASOURCE);
+ requestObject.remove(KEY_NAMESPACE);
+ requestObject.remove(KEY_CATALOG);
+ requestObject.remove(KEY_SCHEMA);
+
+ requestObject.remove(KEY_EXPLAIN);
+ requestObject.remove(KEY_CACHE);
+ } catch (Exception e) {
+ return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
+ }
+
verifier = createVerifier().setVisitor(getVisitor());
if (RequestMethod.isPublicMethod(requestMethod) == false) {
@@ -552,28 +574,6 @@ public M parseResponse(M request) {
}
}
- try {
- setGlobalDatabase(getString(requestObject, KEY_DATABASE));
- setGlobalDatasource(getString(requestObject, KEY_DATASOURCE));
- setGlobalNamespace(getString(requestObject, KEY_NAMESPACE));
- setGlobalCatalog(getString(requestObject, KEY_CATALOG));
- setGlobalSchema(getString(requestObject, KEY_SCHEMA));
-
- setGlobalExplain(getBoolean(requestObject, KEY_EXPLAIN));
- setGlobalCache(getString(requestObject, KEY_CACHE));
-
- requestObject.remove(KEY_DATABASE);
- requestObject.remove(KEY_DATASOURCE);
- requestObject.remove(KEY_NAMESPACE);
- requestObject.remove(KEY_CATALOG);
- requestObject.remove(KEY_SCHEMA);
-
- requestObject.remove(KEY_EXPLAIN);
- requestObject.remove(KEY_CACHE);
- } catch (Exception e) {
- return extendErrorResult(requestObject, e, requestMethod, getRequestURL(), isRoot);
- }
-
final String requestString = JSON.toJSONString(request);//request传进去解析后已经变了
queryResultMap = new HashMap();
@@ -1516,10 +1516,10 @@ else if (childKeys.length == 1 && isTableKey(childKeys[0])) { // 可能无需
JOIN_COPY_KEY_LIST = new ArrayList();
JOIN_COPY_KEY_LIST.add(KEY_ROLE);
JOIN_COPY_KEY_LIST.add(KEY_DATABASE);
+ JOIN_COPY_KEY_LIST.add(KEY_DATASOURCE);
JOIN_COPY_KEY_LIST.add(KEY_NAMESPACE);
JOIN_COPY_KEY_LIST.add(KEY_CATALOG);
JOIN_COPY_KEY_LIST.add(KEY_SCHEMA);
- JOIN_COPY_KEY_LIST.add(KEY_DATASOURCE);
JOIN_COPY_KEY_LIST.add(KEY_COLUMN);
JOIN_COPY_KEY_LIST.add(KEY_NULL);
JOIN_COPY_KEY_LIST.add(KEY_CAST);
@@ -2382,9 +2382,11 @@ protected M batchVerify(RequestMethod method, String tag, int version, String na
}
switch (objAttrKey) {
+ case KEY_DATABASE:
case KEY_DATASOURCE:
+ case KEY_NAMESPACE:
+ case KEY_CATALOG:
case KEY_SCHEMA:
- case KEY_DATABASE:
case KEY_VERSION:
case KEY_ROLE:
objAttrMap.put(objAttrKey, entry.getValue());
@@ -2432,17 +2434,21 @@ protected M batchVerify(RequestMethod method, String tag, int version, String na
}
} else {
setRequestAttribute(key, true, KEY_METHOD, request);
+ setRequestAttribute(key, true, KEY_DATABASE, request);
setRequestAttribute(key, true, KEY_DATASOURCE, request);
+ setRequestAttribute(key, true, KEY_NAMESPACE, request);
+ setRequestAttribute(key, true, KEY_CATALOG, request);
setRequestAttribute(key, true, KEY_SCHEMA, request);
- setRequestAttribute(key, true, KEY_DATABASE, request);
setRequestAttribute(key, true, KEY_VERSION, request);
setRequestAttribute(key, true, KEY_ROLE, request);
}
} else {
setRequestAttribute(key, false, KEY_METHOD, request);
+ setRequestAttribute(key, false, KEY_DATABASE, request);
setRequestAttribute(key, false, KEY_DATASOURCE, request);
+ setRequestAttribute(key, false, KEY_NAMESPACE, request);
+ setRequestAttribute(key, false, KEY_CATALOG, request);
setRequestAttribute(key, false, KEY_SCHEMA, request);
- setRequestAttribute(key, false, KEY_DATABASE, request);
setRequestAttribute(key, false, KEY_VERSION, request);
setRequestAttribute(key, false, KEY_ROLE, request);
}
@@ -2569,7 +2575,9 @@ protected M objectVerify(RequestMethod method, String tag, int version, String n
// 获取指定的JSON结构 >>>>>>>>>>>>>>
M target = wrapRequest(method, tag, object, true);
// Map clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {}
- return getVerifier().setParser(this).verifyRequest(method, name, target, request, maxUpdateCount, getGlobalDatabase(), getGlobalSchema());
+ return getVerifier().setParser(this).verifyRequest(method, name, target, request, maxUpdateCount
+ , getGlobalDatabase(), getGlobalDatasource(), getGlobalNamespace(), getGlobalCatalog(), getGlobalSchema()
+ );
}
/***
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index d9e9eb01..05e9f04e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -142,8 +142,6 @@ public abstract class AbstractSQLConfig, L exte
CONFIG_TABLE_LIST.add(Request.class.getSimpleName());
CONFIG_TABLE_LIST.add(Access.class.getSimpleName());
CONFIG_TABLE_LIST.add(Document.class.getSimpleName());
- CONFIG_TABLE_LIST.add(TestRecord.class.getSimpleName());
-
DATABASE_LIST = new ArrayList<>();
DATABASE_LIST.add(DATABASE_MYSQL);
@@ -5490,7 +5488,7 @@ public static , L extends List> SQLConf
String catalog = getString(request, KEY_CATALOG);
String schema = getString(request, KEY_SCHEMA);
- SQLConfig config = (SQLConfig) callback.getSQLConfig(method, database, schema, datasource, table);
+ SQLConfig config = (SQLConfig) callback.getSQLConfig(method, database, datasource, namespace, catalog, schema, table);
config.setAlias(alias);
config.setDatabase(database); // 不删,后面表对象还要用的,必须放在 parseJoin 前
@@ -5511,9 +5509,9 @@ public static , L extends List> SQLConf
// 对 id, id{}, userId, userId{} 处理,这些只要不为 null 就一定会作为 AND 条件 <<<<<<<<<<<<<<<<<<<<<<<<<
- String idKey = callback.getIdKey(datasource, database, schema, table);
+ String idKey = callback.getIdKey(database, datasource, namespace, catalog, schema, table);
String idInKey = idKey + "{}";
- String userIdKey = callback.getUserIdKey(datasource, database, schema, table);
+ String userIdKey = callback.getUserIdKey(database, datasource, namespace, catalog, schema, table);
String userIdInKey = userIdKey + "{}";
Object idIn = request.get(idInKey); // 可能是 id{}:">0"
@@ -5539,7 +5537,7 @@ public static , L extends List> SQLConf
Object id = request.get(idKey);
if (id == null && method == POST) {
- id = callback.newId(method, database, schema, datasource, table); // null 表示数据库自增 id
+ id = callback.newId(method, database, datasource, namespace, catalog, schema, table); // null 表示数据库自增 id
}
if (id != null) { // null 无效
@@ -6532,44 +6530,59 @@ public static interface IdCallback {
/**为 post 请求新建 id, 只能是 Long 或 String
* @param method
* @param database
+ * @param datasource
+ * @param namespace
+ * @param catalog
* @param schema
* @param table
* @return
*/
- T newId(RequestMethod method, String database, String schema, String datasource, String table);
+ T newId(RequestMethod method, String database, String datasource, String namespace, String catalog, String schema, String table);
/**获取主键名
* @param database
+ * @param datasource
+ * @param namespace
+ * @param catalog
* @param schema
* @param table
* @return
*/
- String getIdKey(String database, String schema, String datasource, String table);
+ String getIdKey(String database, String datasource, String namespace, String catalog, String schema, String table);
/**获取 User 的主键名
* @param database
+ * @param datasource
+ * @param namespace
+ * @param catalog
* @param schema
* @param table
* @return
*/
- String getUserIdKey(String database, String schema, String datasource, String table);
+ String getUserIdKey(String database, String datasource, String namespace, String catalog, String schema, String table);
}
public static interface Callback, L extends List> extends IdCallback {
/**获取 SQLConfig 的实例
* @param method
* @param database
+ * @param datasource
+ * @param namespace
+ * @param catalog
* @param schema
* @param table
* @return
*/
- SQLConfig getSQLConfig(RequestMethod method, String database, String schema, String datasource, String table);
+ SQLConfig getSQLConfig(RequestMethod method, String database, String datasource, String namespace, String catalog, String schema, String table);
/**combine 里的 key 在 request 中 value 为 null 或不存在,即 request 中缺少用来作为 combine 条件的 key: value
+ * @param name
+ * @param request
* @param combine
+ * @param item
* @param key
- * @param request
+ * @throws Exception
*/
void onMissingKey4Combine(String name, M request, String combine, String item, String key) throws Exception;
}
@@ -6583,7 +6596,7 @@ public static abstract class SimpleCallback, L
@SuppressWarnings("unchecked")
@Override
- public T newId(RequestMethod method, String database, String schema, String datasource, String table) {
+ public T newId(RequestMethod method, String database, String datasource, String namespace, String catalog, String schema, String table) {
Long id = System.currentTimeMillis();
if (id <= LAST_ID) {
id = LAST_ID + 1; // 解决高并发下 id 冲突导致新增记录失败
@@ -6594,12 +6607,12 @@ public T newId(RequestMethod method, String database, String schema, String data
}
@Override
- public String getIdKey(String database, String schema, String datasource, String table) {
+ public String getIdKey(String database, String datasource, String namespace, String catalog, String schema, String table) {
return KEY_ID;
}
@Override
- public String getUserIdKey(String database, String schema, String datasource, String table) {
+ public String getUserIdKey(String database, String datasource, String namespace, String catalog, String schema, String table) {
return KEY_USER_ID;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 8a2862eb..3107b905 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -158,6 +158,8 @@ public ResultSet execute(@NotNull Statement statement, String sql) throws Except
return rs;
}
+ public static int MIN_OPTIMIZE_CAPACITY = 2^16 - 1;
+
/**执行SQL
* @param config
* @return
@@ -330,7 +332,7 @@ public M execute(@NotNull SQLConfig config, boolean unknownType) throws
}
else { // 预估容量
capacity = config.getCount() <= 0 ? AbstractParser.MAX_QUERY_COUNT : config.getCount();
- if (capacity > 100) {
+ if (capacity > MIN_OPTIMIZE_CAPACITY) {
// 有 WHERE 条件,条件越多过滤数据越多,暂时不考虑 @combine:"a | (b & !c)" 里面 | OR 和 ! NOT 条件,太复杂也不是很必要
Map> combine = config.getCombineMap();
@@ -354,14 +356,15 @@ public M execute(@NotNull SQLConfig config, boolean unknownType) throws
Map having = config.getHaving();
int havingCount = having == null ? 0 : having.size();
- capacity /= Math.pow(1.5, Math.log10(capacity)
+ double cap = capacity / Math.pow(1.5, Math.log10(capacity)/8 // LIMIT 10^9 = 1 亿 以内无任何条件时 最多扩容 1 次
+ andCondCount
+ ((orCondCount <= 0 ? 0 : 2.0d/orCondCount) // 1: 2.3, 2: 1.5, 3: 1.3, 4: 1.23, 5: 1.18
+ (notCondCount/5.0d) // 1: 1.08, 2: 1.18, 3: 1.28, 4: 1.38, 1.50
- + (groupCount <= 0 ? 0 : 10.0d/groupCount)) // 1: 57.7, 7.6, 3: 3.9, 4: 2.8, 5: 2.3
+ + (groupCount <= 0 ? 0 : 10.0d/Math.min(5, groupCount))) // 1: 57.7, 7.6, 3: 3.9, 4: 2.8, 5: 2.3
+ havingCount
);
- capacity += 1; // 避免正好比需要容量少一点点导致多一次扩容,大量数据 System.arrayCopy
+ cap = groupCount > 0 ? cap : Math.max(MIN_OPTIMIZE_CAPACITY, Math.max(cap, capacity/Math.pow(1.5, 5))); // 1/(1.5^5) = 0.13
+ capacity = (int) (cap + 1); // 避免正好比需要容量少一点点导致多一次扩容,大量数据 System.arrayCopy
}
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 5e665815..81bef3cd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -14,6 +14,7 @@
import static apijson.RequestMethod.HEADS;
import static apijson.RequestMethod.POST;
import static apijson.RequestMethod.PUT;
+import static apijson.orm.AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION;
import static apijson.orm.Operation.*;
//import static apijson.orm.Operation.CODE;
@@ -47,7 +48,6 @@
import apijson.orm.model.AllColumn;
import apijson.orm.model.AllTableComment;
import apijson.orm.model.AllColumnComment;
-import apijson.orm.model.TestRecord;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@@ -108,7 +108,7 @@ public abstract class AbstractVerifier, L exten
// >
// >
@NotNull
- public static Map> SYSTEM_ACCESS_MAP;
+ public static Map> SYSTEM_ACCESS_MAP; // TODO 改名为 CONFIG_ACCESS_MAP ?
@NotNull
public static Map> ACCESS_MAP;
@NotNull
@@ -173,7 +173,6 @@ public abstract class AbstractVerifier, L exten
SYSTEM_ACCESS_MAP.put(ExtendedProperty.class.getSimpleName(), getAccessMap(ExtendedProperty.class.getAnnotation(MethodAccess.class)));
SYSTEM_ACCESS_MAP.put(Document.class.getSimpleName(), getAccessMap(Document.class.getAnnotation(MethodAccess.class)));
- SYSTEM_ACCESS_MAP.put(TestRecord.class.getSimpleName(), getAccessMap(TestRecord.class.getAnnotation(MethodAccess.class)));
}
ACCESS_MAP = new HashMap<>(SYSTEM_ACCESS_MAP);
@@ -209,21 +208,21 @@ public static HashMap getAccessMap(MethodAccess access)
@Override
public String getVisitorIdKey(SQLConfig config) {
- return config == null ? getUserIdKey(null, null, null, null) : config.getUserIdKey();
+ return config == null ? getUserIdKey(null, null, null, null, null, null) : config.getUserIdKey();
}
@Override
- public String getIdKey(String database, String schema, String datasource, String table) {
+ public String getIdKey(String database, String datasource, String namespace, String catalog, String schema, String table) {
return KEY_ID;
}
@Override
- public String getUserIdKey(String database, String schema, String datasource, String table) {
+ public String getUserIdKey(String database, String datasource, String namespace, String catalog, String schema, String table) {
return KEY_USER_ID;
}
@SuppressWarnings("unchecked")
@Override
- public T newId(RequestMethod method, String database, String schema, String datasource, String table) {
+ public T newId(RequestMethod method, String database, String datasource, String namespace, String catalog, String schema, String table) {
return (T) Long.valueOf(System.currentTimeMillis());
}
@@ -319,6 +318,10 @@ public void verifyAllowRole(SQLConfig config, String table, RequestMeth
role = config == null ? UNKNOWN : config.getRole();
}
+ if (Log.DEBUG == false && SYSTEM_ACCESS_MAP.get(table) != null) {
+ throw new IllegalAccessException(table + " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
+ }
+
Map map = ACCESS_MAP.get(table);
if (map == null || Arrays.asList(map.get(method)).contains(role) == false) {
@@ -555,9 +558,9 @@ public void verifyRepeat(String table, String key, Object value, long exceptId)
* @throws Exception
*/
@Override
- public M verifyRequest(@NotNull final RequestMethod method, final String name, final M target, final M request, final int maxUpdateCount
- , final String database, final String schema) throws Exception {
- return verifyRequest(method, name, target, request, maxUpdateCount, database, schema, this, getParser());
+ public M verifyRequest(@NotNull RequestMethod method, String name, M target, M request, int maxUpdateCount
+ , String database, String datasource, String namespace, String catalog, String schema) throws Exception {
+ return verifyRequest(method, name, target, request, maxUpdateCount, database, datasource, namespace, catalog, schema, this, getParser());
}
/**从request提取target指定的内容
@@ -609,27 +612,32 @@ public static , L extends List> M verif
@NotNull RequestMethod method, String name, M target, M request, int maxUpdateCount, String database
, String schema, IdCallback idCallback, @NotNull Parser parser) throws Exception {
- return verifyRequest(method, name, target, request, maxUpdateCount, database, schema, null, idCallback, parser);
+ return verifyRequest(method, name, target, request, maxUpdateCount, database, null, null, null, schema, idCallback, parser);
}
/**从request提取target指定的内容
- * @param method
- * @param name
- * @param target
- * @param request
- * @param maxUpdateCount
- * @param database
- * @param schema
- * @param datasource
- * @param idCallback
- * @param parser
- * @return
- * @param
- * @throws Exception
- */
+ * @param method
+ * @param name
+ * @param target
+ * @param request
+ * @param maxUpdateCount
+ * @param database
+ * @param datasource
+ * @param namespace
+ * @param catalog
+ * @param schema
+ * @param idCallback
+ * @param parser
+ * @return
+ * @param
+ * @param
+ * @param
+ * @throws Exception
+ */
public static , L extends List> M verifyRequest(
@NotNull final RequestMethod method, final String name, final M target, final M request
- , final int maxUpdateCount, final String database, final String schema, final String datasource
- , final IdCallback idCallback, @NotNull Parser parser) throws Exception {
+ , final int maxUpdateCount, final String database, final String datasource, final String namespace
+ , final String catalog, final String schema, final IdCallback idCallback
+ , @NotNull Parser parser) throws Exception {
if (ENABLE_VERIFY_CONTENT == false) {
throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_CONTENT == false" +
" 时不支持校验请求传参内容!如需支持则设置 AbstractVerifier.ENABLE_VERIFY_CONTENT = true !");
@@ -652,7 +660,7 @@ public static , L extends List> M verif
//解析
- return parse(method, name, target, request, database, schema, idCallback, parser, new OnParseCallback() {
+ return parse(method, name, target, request, database, datasource, namespace, catalog, schema, idCallback, parser, new OnParseCallback() {
@Override
public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
@@ -664,19 +672,27 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
}
} else if (isTableKey(key)) {
String db = getString(request, KEY_DATABASE);
- String sh = getString(request, KEY_SCHEMA);
String ds = getString(request, KEY_DATASOURCE);
+ String ns = getString(request, KEY_NAMESPACE);
+ String cl = getString(request, KEY_CATALOG);
+ String sh = getString(request, KEY_SCHEMA);
if (StringUtil.isEmpty(db, false)) {
db = database;
}
- if (StringUtil.isEmpty(sh, false)) {
- sh = schema;
- }
if (StringUtil.isEmpty(ds, false)) {
ds = datasource;
}
+ if (StringUtil.isEmpty(ns, false)) {
+ ns = namespace;
+ }
+ if (StringUtil.isEmpty(cl, false)) {
+ cl = catalog;
+ }
+ if (StringUtil.isEmpty(sh, false)) {
+ sh = schema;
+ }
- String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, key);
+ String idKey = idCallback == null ? null : idCallback.getIdKey(db, ds, ns, cl, sh, key);
String finalIdKey = StringUtil.isEmpty(idKey, false) ? KEY_ID : idKey;
if (method == POST) {
@@ -688,14 +704,14 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
if (Boolean.TRUE.equals(atLeastOne) || RequestMethod.isUpdateMethod(method)) {
verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, atLeastOne != null ? atLeastOne : IS_UPDATE_MUST_HAVE_ID_CONDITION);
- String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, sh, ds, key);
+ String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, ds, ns, cl, sh, key);
String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? KEY_USER_ID : userIdKey;
verifyId(method.name(), name, key, robj, finalUserIdKey, maxUpdateCount, false);
}
}
}
- return verifyRequest(method, key, tobj, robj, maxUpdateCount, database, schema, idCallback, parser);
+ return verifyRequest(method, key, tobj, robj, maxUpdateCount, database, datasource, namespace, catalog, schema, idCallback, parser);
}
@Override
@@ -888,7 +904,7 @@ public static , L extends List> M parse
public static , L extends List> M parse(
@NotNull final RequestMethod method, String name, M target, M real, final String database, final String schema
, final IdCallback idCallback, @NotNull Parser parser, @NotNull OnParseCallback callback) throws Exception {
- return parse(method, name, target, real, database, schema, null, idCallback, parser, callback);
+ return parse(method, name, target, real, database, null, null, null, schema, idCallback, parser, callback);
}
/**对request和response不同的解析用callback返回
* @param method
@@ -896,16 +912,21 @@ public static , L extends List> M parse
* @param target
* @param real
* @param database
- * @param schema
* @param datasource
+ * @param namespace
+ * @param catalog
+ * @param schema
* @param idCallback
* @param parser
* @param callback
* @return
+ * @param
+ * @param
+ * @param
* @throws Exception
*/
public static , L extends List> M parse(@NotNull final RequestMethod method
- , String name, M target, M real, final String database, final String schema, final String datasource
+ , String name, M target, M real, String database, String datasource, String namespace, String catalog, String schema
, final IdCallback idCallback, @NotNull Parser parser, @NotNull OnParseCallback callback) throws Exception {
if (target == null) {
return null;
@@ -1153,18 +1174,26 @@ && rv instanceof List> && isArrayKey(rk)) {
String db = getString(real, KEY_DATABASE);
- String sh = getString(real, KEY_SCHEMA);
String ds = getString(real, KEY_DATASOURCE);
+ String ns = getString(real, KEY_NAMESPACE);
+ String cl = getString(real, KEY_CATALOG);
+ String sh = getString(real, KEY_SCHEMA);
if (StringUtil.isEmpty(db, false)) {
db = database;
}
- if (StringUtil.isEmpty(sh, false)) {
- sh = schema;
- }
if (StringUtil.isEmpty(ds, false)) {
ds = datasource;
}
- String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, name);
+ if (StringUtil.isEmpty(ns, false)) {
+ ns = namespace;
+ }
+ if (StringUtil.isEmpty(cl, false)) {
+ cl = catalog;
+ }
+ if (StringUtil.isEmpty(sh, false)) {
+ sh = schema;
+ }
+ String idKey = idCallback == null ? null : idCallback.getIdKey(db, ds, ns, cl, sh, name);
String finalIdKey = StringUtil.isEmpty(idKey, false) ? KEY_ID : idKey;
// TODO 放在operate前?考虑性能、operate修改后再验证的值是否和原来一样
@@ -1298,7 +1327,7 @@ && rv instanceof List> && isArrayKey(rk)) {
}
if (nkl.contains(k) || real.get(k) != null) {
- real = parse(method, name, (M) v, real, database, schema, datasource, idCallback, parser, callback);
+ real = parse(method, name, (M) v, real, database, datasource, namespace, catalog, schema, idCallback, parser, callback);
}
}
}
@@ -1309,6 +1338,11 @@ && rv instanceof List> && isArrayKey(rk)) {
}
public static ScriptEngine getScriptEngine(String lang) {
+ if (ENABLE_SCRIPT_FUNCTION == false) {
+ throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION" +
+ " == false 时不支持执行脚本!如需支持则设置为 true !");
+ }
+
boolean isEmpty = StringUtil.isEmpty(lang, true);
ScriptEngine engine = isEmpty ? SCRIPT_ENGINE : SCRIPT_ENGINE_MANAGER.getEngineByName(lang);
diff --git a/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java b/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java
new file mode 100755
index 00000000..f8935516
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java
@@ -0,0 +1,29 @@
+/*Copyright (C) 2020 Tencent. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+//package apijson.orm;
+//
+//import apijson.MethodAccess;
+//
+//import java.lang.annotation.Documented;
+//import java.lang.annotation.Inherited;
+//import java.lang.annotation.Retention;
+//import java.lang.annotation.Target;
+//
+//import static apijson.orm.AbstractVerifier.*;
+//import static java.lang.annotation.ElementType.TYPE;
+//import static java.lang.annotation.RetentionPolicy.RUNTIME;
+//
+///**配置表的请求方法权限,只允许某些角色通过对应方法访问
+// 不能直接查到 MethodAccess,需要往上递归查找
+// * @author Lemon
+// */
+//@Documented
+//@Retention(RUNTIME)
+//@Target(TYPE)
+//@Inherited
+//@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
+//public @interface ConfigMethodAccess {
+//}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java
index b69eb9a7..45fcbf47 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Operation.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java
@@ -124,9 +124,16 @@ public enum Operation {
* 例如 "sex != 0 && sex != 1": "throw new Error('sex 必须在 [0, 1] 内!')"
* 自定义代码,当满足条件是执行后面的代码
*
+ * 还可以指定语言,例如 "python:sex not in(0, 1)": "throw new Error('sex 必须在 [0, 1] 内!')"
+ *
* 还有
* "ELSE": ""
* 自定义代码,不处理,和不传一样
+ *
+ * 需要
+ * 1.AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true 启用
+ * 2.JDK 8~13 可用自带 Nashorn 这个 js 引擎,注意配置 ClassFilter 防脚本注入攻击;
+ * 其它语言及 JDK 14+ 都必须依赖外部脚本引擎,注意按对应引擎说明方式防脚本注入攻击,最好是沙箱环境。
*/
IF,
diff --git a/APIJSONORM/src/main/java/apijson/orm/Verifier.java b/APIJSONORM/src/main/java/apijson/orm/Verifier.java
index 4b926519..97db58a9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Verifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Verifier.java
@@ -69,14 +69,17 @@ public interface Verifier, L extends List, L e
@Override
public ScriptExecutor init() {
- ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
- scriptEngine = scriptEngineManager.getEngineByName(scriptEngineName());
+ scriptEngine = createScriptEngine();
return this;
}
+ protected ScriptEngine createScriptEngine() {
+ String name = scriptEngineName();
+ if ("nashorn".equalsIgnoreCase(name) || "javascript".equalsIgnoreCase(name)
+ || "js".equalsIgnoreCase(name) || "ecmascript".equalsIgnoreCase(name)) {
+ try {
+ Class> factoryClass = Class.forName("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+ Class> filterClass = Class.forName("jdk.nashorn.api.scripting.ClassFilter");
+ Object filter = java.lang.reflect.Proxy.newProxyInstance(
+ filterClass.getClassLoader(),
+ new Class>[]{filterClass},
+ (proxy, method, methodArgs) -> isClassExposureAllowed((String) methodArgs[0]));
+ Object factory = factoryClass.getDeclaredConstructor().newInstance();
+ return (ScriptEngine) factoryClass.getMethod("getScriptEngine", filterClass).invoke(factory, filter);
+ } catch (Throwable e) {
+ Log.e(TAG, "create sandboxed Nashorn engine failed, falling back: " + e);
+ }
+ }
+ return new ScriptEngineManager().getEngineByName(name);
+ }
+
+ protected boolean isClassExposureAllowed(String className) {
+ return false;
+ }
+
protected abstract String scriptEngineName();
protected abstract Object extendParameter(AbstractFunctionParser parser, Map currentObject, String methodName, Object[] args);
diff --git a/Document-Chinese.md b/Document-Chinese.md
index 11db6fe8..ffe8e975 100644
--- a/Document-Chinese.md
+++ b/Document-Chinese.md
@@ -1,5 +1,4 @@
-[English](https://github.com/Tencent/APIJSON/blob/master/Document-English.md)
-
+## 中文 | [English](https://github.com/Tencent/APIJSON/blob/master/Document.md)
# APIJSON 通用文档
本文是通用文档,只和 APIJSON 协议有关,和 C#, Go, Java, JavaScript, PHP, Python, TypeScript 等开发语言无关。
diff --git a/Document.md b/Document.md
index 7574e569..d3f18d4a 100644
--- a/Document.md
+++ b/Document.md
@@ -1,3 +1,5 @@
+## English | [中文](https://github.com/Tencent/APIJSON/blob/master/Document-Chinese.md)
+
#### A better online document is available at https://apijsondocs.readthedocs.io
### Examples:
diff --git a/README-Chinese.md b/README-Chinese.md
index efd1858f..c13f82e9 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -14,6 +14,9 @@ This source code is licensed under the Apache License Version 2.0
视频教程
测试用例
AI 问答
+ Skills
+ MCP
+ A2A
@@ -376,117 +379,14 @@ https://github.com/Tencent/APIJSON/issues/187
### 贡献者们
主项目 APIJSON 的贡献者们(6 个腾讯工程师、1 个微软工程师、1 个阿里云工程师、1 个字节跳动工程师、1 个网易工程师、1 个 Zoom 工程师、1 个圆通工程师、1 个知乎基础研发架构师、1 个智联招聘工程师、gorm-plus 作者、1 个美国加州大学学生、3 个 SUSTech 学生等):
https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
生态周边项目的作者们(2 个腾讯工程师、1 个 BAT 技术专家、1 个微软工程师、2 个字节跳动工程师、1 个神州数码工程师&Apache dubbo2js 作者 等):
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
还有为 APIJSON 扫描代码贡献 Issue 的 [蚂蚁集团源伞](https://www.sourcebrella.com) 和 [奇安信代码卫士](https://github.com/QiAnXinCodeSafe)
@@ -516,7 +416,7 @@ https://github.com/Tencent/APIJSON/blob/master/Roadmap.md
[OceanBase](https://www.oceanbase.com/docs/oceanbase/V2.2.50/ss-sr-select_daur3l), [Spark](https://spark.apache.org/docs/3.3.0/sql-ref-syntax-qry-select.html)(可用 Hive 对接), [Phoenix](http://phoenix.apache.org/language/index.html#select)(延伸支持 HBase)
### 我要赞赏
-创作不易,坚持更难,右上角点 ⭐Star 来支持/收藏下吧,谢谢 ^_^
+创作不易,坚持更难,右上角点亮 ⭐ Star 来收藏/支持下吧,谢谢 ^_^
https://github.com/Tencent/APIJSON
@@ -642,6 +542,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[3分钟掌握APIJSON搜索黑科技:从模糊匹配到智能检索](https://blog.csdn.net/gitblog_00009/article/details/152403741)
+[省心省力的后端神器——APIJSON.NET](https://mp.weixin.qq.com/s/8-E-a18NttdA0AAqasE0AQ)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
@@ -697,9 +599,11 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-node](https://github.com/kevinaskin/apijson-node) 字节跳动工程师开源的 Node.ts 版 APIJSON,提供 nestjs 和 typeorm 的 Demo 及后台管理
+[nestjs-apijson](https://github.com/yangzhouQS/nestjs-apijson) APIJSON NestJS 版,支持 CRUD、JOIN、各种条件,适配 MySQL, PostgreSQL, SQLite (AI 辅助)
+
[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) Python 版 APIJSON,支持 MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等
-[apijson-rust](https://gitee.com/APIJSON/panda-base) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL
+[apijson-rust](https://github.com/APIJSON/apijson-rust) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL (AI 辅助)
[APIJSONParser](https://github.com/Zerounary/APIJSONParser) 第三方 APIJSON 解析器,将 JSON 动态解析成 SQL
@@ -735,7 +639,9 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[ApiJsonByJFinal](https://gitee.com/zhiyuexin/ApiJsonByJFinal) 整合 APIJSON 和 JFinal 的 Demo
-[apijson-go-demo](https://github.com/glennliao/apijson-go-demo) apijson-go demos,提供 3 个从简单到复杂的不同场景 Demo
+[bookmark](https://github.com/glennliao/bookmark) goframe + apijson-go + vue3 + antd vue 4 的在线书签
+
+[apijson-go-demo](https://github.com/APIJSON/apijson-go-demo) apijson-go demos,提供 3 个从简单到复杂的不同场景 Demo
[apijson-builder](https://github.com/pengxianggui/apijson-builder) 一个方便为 APIJSON 构建 RESTful 请求的 JavaScript 库
@@ -751,7 +657,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[xyerp](https://gitee.com/yinjg1997/xyerp) 基于ApiJson的低代码ERP
-[quick-boot](https://github.com/csx-bill/quick-boot) 基于 Spring Cloud 2022、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
+[quick-boot](https://github.com/csx-bill/quick-boot/tree/master) 基于 Spring Cloud 2022、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) 一个快速构建 APIJSON 查询条件的插件
@@ -765,7 +671,9 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-spring-boot](https://gitee.com/yunjiao-source/apijson-spring-boot) Springboot3 for APIJSON,用 YAML 简化代码配置
-感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
+[APIJSONServer](https://github.com/cyber2jie/APIJSONServer) 基于APIJSON实现的数据服务端
+
+感谢热心的作者们的贡献,点亮 ⭐ Star 收藏/支持下他们吧~
### 腾讯犀牛鸟开源人才培养计划
diff --git a/README.md b/README.md
index 8811a3bd..0bcb1841 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,9 @@ This source code is licensed under the Apache License Version 2.0
Video
Test
Ask AI
+
Skills
+
MCP
+
A2A
@@ -75,7 +78,7 @@ This source code is licensed under the Apache License Version 2.0
-
+
@@ -141,7 +144,7 @@ You're gonna leave 'em all in awe, awe, awe.
**Tired with endless arguments about HTTP API dev or use?**
**Use APIJSON-the ORM for providing infinity codeless CRUD APIs that fit almost all your needs.**
-**Unfold the Power(In Your Soul) with ⭐Star & Clone.**
+**Unfold the Power(In Your Soul) with ⭐ Star & Clone.**
### APIJSON Show
#### Postman test APIJSON
@@ -215,7 +218,7 @@ Please have a look at the [open issues](https://github.com/Tencent/APIJSON/issue
Fork the project and send a pull request.
-Please also ⭐Star the project!
+### Please also ⭐ Star(on the top right) this project!
##
5. Releases
@@ -301,97 +304,14 @@ https://github.com/Tencent/APIJSON/issues/187
### Contributers of APIJSON:
Contributers for the APIJSON core project(6 Tencent engineers, 1 Microsoft engineer, 1 Zhihu architect, 1 Bytedance(TikTok) engineer, 1 NetEase engineer, 1 Zoom engineer, 1 YTO Express engineer, 1 Zhilian engineer, 1 UC student、3 SUSTech students, etc.):
https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) expert, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Thanks to all contributers of APIJSON!
@@ -405,5 +325,136 @@ a lot of employees from big famous companies(Tencent, Huawei, Microsoft, Zoom, e
+### Ecosystem
+[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) Demo projects with document and SQL files for APIJSON with different programming languages and different frameworks
+
+[apijson-orm](https://github.com/APIJSON/apijson-orm) APIJSON ORM library, Maven, Gradle, etc can be used for dependencies
+
+[apijson-framework](https://github.com/APIJSON/apijson-framework) APIJSON Server Framework for configuring access of roles and validation of arguments in database tables, then using APIJSON easier
+
+[apijson-router](https://github.com/APIJSON/apijson-router) A router plugin for APIJSON, expose undercontrolled RESTful-like HTTP API to public network, transfer to APIJSON request and execute
+
+[apijson-column](https://github.com/APIJSON/apijson-column) A column plugin for Tencent APIJSON, supports Column Inverse and Column Mapping
+
+[apijson-jackson](https://github.com/APIJSON/apijson-jackson) A jackson plugin for APIJSON
+
+[apijson-fastjson2](https://github.com/APIJSON/apijson-fastjson2) A fastjson2 plugin for APIJSON
+
+[apijson-gson](https://github.com/APIJSON/apijson-gson) A gson plugin for APIJSON
+
+[apijson-milvus](https://github.com/APIJSON/apijson-milvus) An APIJSON plugin for Milvus - An AI vector database
+
+[apijson-influxdb](https://github.com/APIJSON/apijson-influxdb) An APIJSON plugin for InfluxDB - An IoT time-series database
+
+[apijson-mongodb](https://github.com/APIJSON/apijson-mongodb) An APIJSON plugin for MongoDB - A NoSQL database
+
+[apijson-cassandra](https://github.com/APIJSON/apijson-cassandra) An APIJSON plugin for Cassandra - A NoSQL database
+
+[APIAuto](https://github.com/TommyLemon/APIAuto) ☔ The most advanced tool for HTTP API. Machine learning no-code testing and AI assistant, generating codes and static analysis, generating comments and floating hints. Used by Tencent, SHEIN, TRANSSION, etc
+
+[CVAuto](https://github.com/TommyLemon/CVAuto) 👁 No-code, zero-annotation CV(Computer Vision) AI automated testing tool 🚀 Eliminates the need for extensive manual tasks such as drawing bounding boxes and labeling for image recognition algorithms
+
+[UnitAuto](https://github.com/TommyLemon/UnitAuto) ☀️ The most advanced unit testing way powered by machine learning. Coding-free, comprehensive and automatic testing for methods/functions. Used by Tencent, Kwai, a Fortune 500 company, etc
+
+[SQLAuto](https://github.com/TommyLemon/SQLAuto) 🔍 A smart SQL testing automation tool for databases, supports any CRUD, any template variables, generating argument combinations, generating lots of data rows
+
+[UIGO](https://github.com/TommyLemon/UIGO) 📱 Coding-free, fast, accurate and stable UI replayer 🚀 Incredible ±3px auto locating and ±2ms auto waiting. Used by Tencent, invited by WeChat team to share
+
+[APIJSONdocs](https://github.com/ruoranw/APIJSONdocs) APIJSON English documentation, provided a website to view
+
+[apijson-doc](https://github.com/vincentCheng/apijson-doc) APIJSON Chinese documentation, provided a website to view
+
+[apijson.org](https://github.com/APIJSON/apijson.org) APIJSON official website
+
+[APIJSON.NET](https://github.com/liaozb/APIJSON.NET) APIJSON for C#, supports CRUD for MySQL, PostgreSQL, SQL Server, Oracle, SQLite
+
+[apijson-go](https://github.com/glennliao/apijson-go) APIJSON for Go, based on Go(>=1.18) + GoFrame2, support CRUD
+
+[apijson-go](https://gitee.com/tiangao/apijson-go) APIJSON for Go, support CRUD, can directly run in Docker
+
+[apijson-hyperf](https://github.com/kvnZero/hyperf-APIJSON.git) APIJSON for PHP, based on Hyperf, supports MySQL
+
+[APIJSON-php](https://github.com/xianglong111/APIJSON-php) APIJSON for PHP, based on ThinkPHP,supports MySQL, PostgreSQL, SQL Server, Oracle, etc
+
+[apijson-php](https://github.com/qq547057827/apijson-php) APIJSON for PHP, based on ThinkPHP,supports MySQL, PostgreSQL, SQL Server, Oracle, etc
+
+[apijson-node](https://github.com/kevinaskin/apijson-node) APIJSON for Node.js, developed by a ByteDance engineer, provides demos for NestJS and Typeorm, as well as backend management
+
+[nestjs-apijson](https://github.com/yangzhouQS/nestjs-apijson) APIJSON for NestJS, supports CRUD, Joins for MySQL, PostgreSQL, SQLite (AI assisted)
+
+[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) APIJSON for Python, supports CRUD for MySQL, PostgreSQL, SQL Server, Oracle, SQLite, etc
+
+[apijson-rust](https://github.com/APIJSON/apijson-rust) APIJSON for Rust, supports CRUD for MySQL and PostgreSQL (AI assisted)
+
+[APIJSONParser](https://github.com/Zerounary/APIJSONParser) An APIJSON Parser, dynamically parses JSON to SQL
+
+[FfApiJson](https://gitee.com/own_3_0/ff-api-json) An APIJSON Parser, parses JSON to SQL, supports multiple data sources
+
+[APIJSON-ToDo-Demo](https://github.com/jerrylususu/apijson_todo_demo) A simple TODO demo for APIJSON with customized authorization and authentication
+
+[apijson-learn](https://github.com/rainboy-learn/apijson-learn) APIJSON study note and analysis for source code
+
+[apijson-practice](https://github.com/vcoolwind/apijson-practice) A library for APIJSON parameter validation annotations and related demos, open-sourced by a BAT expert
+
+[apijson-db2](https://github.com/andream7/apijson-db2) Demo of APIJSON + IBM DB2 database, open-sourced by a Microsoft engineer
+
+[APIJSONDemo](https://github.com/qiujunlin/APIJSONDemo) Demo of APIJSON + ClickHouse database, open-sourced by a ByteDance engineer
+
+[APIJSONDemo_ClickHouse](https://github.com/chenyanlann/APIJSONDemo_ClickHouse) Demo for APIJSON + SpringBoot + ClickHouse
+
+[APIJSONBoot_Hive](https://github.com/chenyanlann/APIJSONBoot_Hive) Demo for APIJSON + SpringBoot + Hive
+
+[apijson-sample](https://gitee.com/greyzeng/apijson-sample) Simple demo and tutorial for using APIJSON
+
+[apijson-examples](https://gitee.com/drone/apijson-examples) Demo for APIJSON with frontend web page, backend server and management system
+
+[apijson-ruoyi](https://github.com/daodol/apijson-ruoyi) APIJSON + RuoYi, provides online maintenance for database configuration, etc
+
+[light4j](https://github.com/xlongwei/light4j) Demo for APIJSON + Redis + light-4j - a microservices framework
+
+[SpringServer1.2-APIJSON](https://github.com/Airforce-1/SpringServer1.2-APIJSON) The smart Party building server provides interfaces for uploading and downloading files
+
+[apijson_template](https://github.com/abliger/apijson_template) APIJSON Java template, using Gradle to manage dependencie and building apps
+
+[api-json-demo](https://gitee.com/hxdwd/api-json-demo) Demo for APIJSON to replace traditional ORM, compated Oracle transactions
+
+[ApiJsonByJFinal](https://gitee.com/zhiyuexin/ApiJsonByJFinal) Demo for APIJSON + JFinal - a popular web framework
+
+[bookmark](https://github.com/glennliao/bookmark) Online bookmark using goframe + apijson-go + vue3 + antd vue 4
+
+[apijson-go-demo](https://github.com/APIJSON/apijson-go-demo) Demo for apijson-go
+
+[apijson-go-ui](https://github.com/glennliao/apijson-go-ui) apijson-go UI config, supports access control, request rule configuration, etc
+
+[apijson-builder](https://github.com/pengxianggui/apijson-builder) A JavaScript client library providing RESTful-like functions for APIJSON
+
+[AbsGrade](https://github.com/APIJSON/AbsGrade) List cascading algorithm supports single-level comments in WeChat Moments, double-level comments in QQ Space, and multi-level (unlimited-level) folders in Baidu Cloud
+
+[APIJSON-Android-RxJava](https://github.com/TommyLemon/APIJSON-Android-RxJava) Practical project mimicking WeChat Moments updates, based on ZBLibrary(UI) + APIJSON(HTTP) + RxJava(Data)
+
+[Android-ZBLibrary](https://github.com/TommyLemon/Android-ZBLibrary) 🔥 An Android MVP Framework with many demos, detailed documents, simple usages and strict codes
+
+[apijson-dynamic-datasource](https://github.com/wb04307201/apijson-dynamic-datasource) Based on APIJSON, this demo demonstrates dynamic data source switching and batch operations on the same data source to ensure transaction consistency
+
+[xyerp](https://gitee.com/yinjg1997/xyerp) Low-code ERP based on APIJSON
+
+[quick-boot](https://github.com/csx-bill/quick-boot/tree/master) Low-code system based on Spring Cloud 2022 + Spring Boot 3 + AMIS + APIJSON
+
+[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) A plugin for quickly building APIJSON query conditions
+
+[apijson-builder](https://github.com/yeli19950109/apijson-builder) A simplified TypeScript wrapper for APIJSON, easier to remember than directly constructing query JSON
+
+[lanmuc](https://gitee.com/element-admin/lanmuc) A platform for producing low-code backend APIs, compatible with both configuration-based and code-based APIs, enabling rapid API production and project deployment
+
+[review_plan](https://gitee.com/PPXcodeTry/review_plan) Review Reminder Web Version (Java Technology Practice Project)
+
+[apijson-nutz](https://github.com/vincent109/apijson-nutz) Demo for APIJSON + Nutz + NutzBoot
+
+[apijson-spring-boot](https://gitee.com/yunjiao-source/apijson-spring-boot) Springboot3 for APIJSON, using YAML to simplify configuration
+
+[APIJSONServer](https://github.com/cyber2jie/APIJSONServer) Data server based on APIJSON
+
+Thank you to all the enthusiastic authors for the contributions~
+### Please give them a ⭐ Star(on the top right) to support their hard works!
diff --git a/context7.json b/context7.json
new file mode 100644
index 00000000..7059d93f
--- /dev/null
+++ b/context7.json
@@ -0,0 +1,4 @@
+{
+ "url": "https://context7.com/tencent/apijson",
+ "public_key": "pk_hqcWKqm5UmWzQouumDz3F"
+}