fix(ddic): create 命令消费 JSON description(--description 优先→JSON→回退名);upload testclasses XML 包裹通道(S4T 三轮实证);SKILL 错误速查表 +4 条 NW740 实证(ADT 创建不持久化文本/classes 激活空响应/unknown comments/STRG+comptype 陷阱)
This commit is contained in:
@@ -859,6 +859,27 @@ def cmd_create(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
source_file: str | None = getattr(args, "source", None)
|
||||
definition_file: str | None = getattr(args, "definition", None)
|
||||
|
||||
# JSON definition description: --description wins, then JSON, then name.
|
||||
# (NW740 ADT create does not persist texts anyway — see SKILL.md known
|
||||
# limitation; this only affects the XML body sent at creation time.)
|
||||
if (
|
||||
definition_file
|
||||
and not getattr(args, "description", None)
|
||||
and obj_type in DDIC_TYPES
|
||||
):
|
||||
import json as _json
|
||||
import os as _os
|
||||
|
||||
if _os.path.isfile(definition_file):
|
||||
try:
|
||||
with open(definition_file, "r", encoding="utf-8") as f:
|
||||
_data = _json.load(f)
|
||||
_json_desc = _data.get("description")
|
||||
if _json_desc:
|
||||
description = _json_desc
|
||||
except Exception as _e:
|
||||
logger.warning("读取定义文件 description 失败: %s", _e)
|
||||
|
||||
type_label = get_type_config(obj_type).label
|
||||
|
||||
if obj_type == "function":
|
||||
|
||||
@@ -33,6 +33,7 @@ def cmd_upload(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
obj_type: str = args.type
|
||||
file_path: str = args.path
|
||||
corr_nr: str | None = getattr(args, "corr_nr", None)
|
||||
part: str | None = getattr(args, "part", None)
|
||||
|
||||
parsed = parse_object_name(name, obj_type)
|
||||
type_label = get_type_config(obj_type).label
|
||||
@@ -42,6 +43,16 @@ def cmd_upload(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
print(f" functiongroup 等是容器对象,不包含可编辑的源代码文件")
|
||||
raise InvalidNameError(f"{type_label}没有源代码,不支持 upload 操作")
|
||||
|
||||
src_uri = parsed.src_uri
|
||||
if part == "testclasses":
|
||||
# Local test classes include (ABAP Unit): classes only.
|
||||
# ADT unit testruns only discovers LOCAL test classes — global
|
||||
# test classes are NOT run by the endpoint (S4T proven).
|
||||
if obj_type != "class":
|
||||
print(f"\n ✗ --part testclasses 仅支持 class 类型")
|
||||
raise InvalidNameError("--part testclasses 仅支持 class 类型")
|
||||
src_uri = f"/sap/bc/adt/oo/classes/{name.lower()}/source/testclasses"
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
print(f"\n ✗ 文件不存在: {file_path}")
|
||||
raise ConfigError(f"文件不存在: {file_path}")
|
||||
@@ -90,7 +101,30 @@ def cmd_upload(args: argparse.Namespace, client: ADTClient) -> None:
|
||||
print(f" ✓ 锁定成功(传输请求: {corr_nr})")
|
||||
|
||||
try:
|
||||
client.set_source(parsed.src_uri, source, lock_handle, corr_nr)
|
||||
if part == "testclasses":
|
||||
# testclasses endpoint expects XML body: <abapClass> element
|
||||
# in the oo/classes namespace wrapping the raw source.
|
||||
from xml.sax.saxutils import escape as _esc
|
||||
|
||||
xml_body = (
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'
|
||||
'<cls:abapClass xmlns:cls="http://www.sap.com/adt/oo/classes" '
|
||||
'xmlns:adtcore="http://www.sap.com/adt/core" '
|
||||
'adtcore:type="testclassInclude" '
|
||||
'adtcore:description="Local test classes" '
|
||||
'adtcore:name="zint_cl_route_engine">'
|
||||
f"<cls:source>{_esc(source)}</cls:source>"
|
||||
"</cls:abapClass>"
|
||||
)
|
||||
client.set_source(
|
||||
src_uri,
|
||||
xml_body,
|
||||
lock_handle,
|
||||
corr_nr,
|
||||
content_type="application/vnd.sap.adt.abap.classes+xml; charset=utf-8",
|
||||
)
|
||||
else:
|
||||
client.set_source(src_uri, source, lock_handle, corr_nr)
|
||||
print(f" ✓ 源代码写入成功")
|
||||
finally:
|
||||
client.unlock(parsed.obj_uri, lock_handle)
|
||||
|
||||
Reference in New Issue
Block a user