bk-schema-checker 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/py/load_backend.py +48 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bk-schema-checker",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "对照前端 services、Django REST 序列化器,以及可选的 bk-local-mock 响应,做接口协议检查。不启动 Django。",
5
5
  "type": "module",
6
6
  "author": "austinqli",
@@ -43,4 +43,4 @@
43
43
  "check": "node ./bin/bk-schema-checker.js",
44
44
  "test": "node --test test/"
45
45
  }
46
- }
46
+ }
@@ -938,7 +938,9 @@ class BackendLoader:
938
938
  if func is not None and is_paginated(func):
939
939
  return True
940
940
  if spec["action"] == "list" and not self.pagination_disabled(ref):
941
- return self.class_assign_mro(ref, "pagination_class") is not None
941
+ # DRF ListModelMixin.list 在 pagination_class 非空时包成 {count, results}。
942
+ # 类上没写则用 REST_FRAMEWORK["DEFAULT_PAGINATION_CLASS"],和查询参数同源。
943
+ return self.pagination_source(ref, spec) is not None
942
944
  return False
943
945
 
944
946
  def pagination_disabled_here(self, ref):
@@ -1378,10 +1380,48 @@ class BackendLoader:
1378
1380
  schema = self.materialize(ref, mode, stack)
1379
1381
  if schema.get("type") == "array":
1380
1382
  return {"schema": schema, "many": True, "empty": False}
1383
+ if not many and mode == "response" and isinstance(self.swagger_example(ref), list):
1384
+ many = True
1381
1385
  if many:
1382
1386
  schema = {"type": "array", "items": schema}
1383
1387
  return {"schema": schema, "many": many, "empty": False}
1384
1388
 
1389
+ def swagger_example(self, ref):
1390
+ if not ref:
1391
+ return None
1392
+ meta = nested_class(ref.node, "Meta")
1393
+ if not meta:
1394
+ return None
1395
+ node = class_assign(meta, "swagger_schema_fields")
1396
+ if node is None:
1397
+ return None
1398
+ value = self.eval_const(node, ref.module, set())
1399
+ if not isinstance(value, dict):
1400
+ return None
1401
+ return value.get("example")
1402
+
1403
+ def schema_from_swagger_example(self, ref):
1404
+ example = self.swagger_example(ref)
1405
+ if not isinstance(example, list):
1406
+ return None
1407
+ return {"type": "array", "items": self.example_item_schema(example), "required": True}
1408
+
1409
+ def example_item_schema(self, example):
1410
+ if not example:
1411
+ return any_schema()
1412
+ first = example[0]
1413
+ if isinstance(first, dict):
1414
+ return {"type": "object", "fields": {}, "open": True, "required": True}
1415
+ if isinstance(first, bool):
1416
+ return {"type": "boolean", "required": True, "allowNull": False}
1417
+ if isinstance(first, int):
1418
+ return {"type": "integer", "required": True, "allowNull": False}
1419
+ if isinstance(first, float):
1420
+ return {"type": "number", "required": True, "allowNull": False}
1421
+ if isinstance(first, str):
1422
+ return {"type": "string", "required": True, "allowNull": False}
1423
+ return any_schema()
1424
+
1385
1425
  def materialize(self, ref, mode, stack):
1386
1426
  key = f"{ref.module.file}#{ref.name}"
1387
1427
  if key in stack:
@@ -1412,7 +1452,13 @@ class BackendLoader:
1412
1452
  if mode == "response" and built.get("writeOnly"):
1413
1453
  continue
1414
1454
  fields[field_name] = built
1415
- return {"type": "object", "fields": fields, "serializer": ref.name, "required": True}
1455
+ schema = {"type": "object", "fields": fields, "serializer": ref.name, "required": True}
1456
+ if mode == "response" and not fields:
1457
+ hinted = self.schema_from_swagger_example(ref)
1458
+ if hinted:
1459
+ hinted.setdefault("serializer", ref.name)
1460
+ return hinted
1461
+ return schema
1416
1462
  finally:
1417
1463
  stack.discard(key)
1418
1464