mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-04 18:50:23 +03:00
feat(meta-decompile,meta-compile): поддержка WebService и полнота его формата
meta-decompile не знал тип WebService — 18 сервисов УТ не проходили раундтрип.
Добавлен разбор (оба порта): namespace, состав XDTO-пакетов, дескриптор,
операции с параметрами.
Компилятор поддерживал тип поверхностно; раундтрип на реальных сервисах
показал, чего не хватало:
- XDTOPackages эмитился скаляром, а это СПИСОК элементов: ссылка на пакет
конфигурации (xr:MDObjectRef) либо URI внешнего пространства имён
(xs:string). Presentation пуст, CheckState 0 — 19/19 по корпусу;
- DescriptorFileName не эмитился вовсе, хотя есть у всех 18 сервисов и НЕ
выводится из имени (DMILService -> dmil.1cws) — нужен явный ключ;
- DataLockControlMode не эмитился (Managed у всех 192 операций);
- Comment не эмитился ни у сервиса, ни у операций и параметров;
- типы из собственного пространства имён (81 случай) писались без локального
xmlns. В DSL задаются нотацией Кларка "{uri}ИмяТипа", компилятор объявляет
xmlns сам — как это делает платформа.
Nillable захватывается явно у операций и параметров: по корпусу значения
смешанные (операции 103/89, параметры 128/395), дефолт угадать нельзя.
Порядок операций и параметров приведён к порядку DSL в обоих портах — PS шёл
в порядке хеш-таблицы, py сортировал.
Проверка: 25 сервисов УТ (18 WebService + 7 HTTPService) 25/25 без
расхождений; JSON декомпилятора побайтово совпадает у PS и py на всех 25;
сюита 76/76 на обоих портах; 1С-сертификация обоих кейсов пройдена.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
01c45581ef
commit
87f97e986a
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.71 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.72 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -3812,19 +3812,51 @@ function Emit-HTTPServiceProperties {
|
||||
X "$i<SessionMaxAge>$sessionMaxAge</SessionMaxAge>"
|
||||
}
|
||||
|
||||
# XDTO-тип значения: "{uri}Local" (нотация Кларка) → тег с ЛОКАЛЬНЫМ объявлением xmlns, как пишет
|
||||
# платформа (<XDTOValueType xmlns:d1p1="uri">d1p1:Local</...>). Простой "xs:string" — как есть.
|
||||
function Emit-XDTOType {
|
||||
param([string]$indent, [string]$tag, [string]$value)
|
||||
if ($value -match '^\{([^}]+)\}(.+)$') {
|
||||
$uri = $Matches[1]; $local = $Matches[2]
|
||||
X "$indent<$tag xmlns:d1p1=`"$(Esc-Xml $uri)`">d1p1:$(Esc-Xml $local)</$tag>"
|
||||
} else {
|
||||
X "$indent<$tag>$(Esc-Xml $value)</$tag>"
|
||||
}
|
||||
}
|
||||
|
||||
function Emit-WebServiceProperties {
|
||||
param([string]$indent)
|
||||
$i = $indent
|
||||
|
||||
X "$i<Name>$(Esc-Xml $objName)</Name>"
|
||||
Emit-MLText $i "Synonym" $synonym
|
||||
X "$i<Comment/>"
|
||||
if ($def.comment) { X "$i<Comment>$(Esc-Xml "$($def.comment)")</Comment>" } else { X "$i<Comment/>" }
|
||||
|
||||
$namespace = if ($def.namespace) { "$($def.namespace)" } else { "" }
|
||||
X "$i<Namespace>$(Esc-Xml $namespace)</Namespace>"
|
||||
|
||||
$xdtoPackages = if ($def.xdtoPackages) { "$($def.xdtoPackages)" } else { "" }
|
||||
if ($xdtoPackages) { X "$i<XDTOPackages>$xdtoPackages</XDTOPackages>" } else { X "$i<XDTOPackages/>" }
|
||||
# XDTOPackages — СПИСОК элементов, а не скаляр: значение либо ссылка на пакет конфигурации
|
||||
# (xr:MDObjectRef "XDTOPackage.Имя"), либо URI внешнего пространства имён (xs:string).
|
||||
# Presentation всегда пуст, CheckState всегда 0 (проверено на корпусе: 19/19).
|
||||
$pkgs = @()
|
||||
if ($def.xdtoPackages) { $pkgs = @($def.xdtoPackages) }
|
||||
if ($pkgs.Count -gt 0) {
|
||||
X "$i<XDTOPackages>"
|
||||
foreach ($p in $pkgs) {
|
||||
$pv = "$p"
|
||||
$xt = if ($pv -like 'XDTOPackage.*') { 'xr:MDObjectRef' } else { 'xs:string' }
|
||||
X "$i`t<xr:Item>"
|
||||
X "$i`t`t<xr:Presentation/>"
|
||||
X "$i`t`t<xr:CheckState>0</xr:CheckState>"
|
||||
X "$i`t`t<xr:Value xsi:type=`"$xt`">$(Esc-Xml $pv)</xr:Value>"
|
||||
X "$i`t</xr:Item>"
|
||||
}
|
||||
X "$i</XDTOPackages>"
|
||||
} else { X "$i<XDTOPackages/>" }
|
||||
|
||||
# Имя файла дескриптора не выводится из имени сервиса (DMILService → dmil.1cws) — только дефолт.
|
||||
$descriptor = if ($def.descriptorFileName) { "$($def.descriptorFileName)" } else { "$objName.1cws" }
|
||||
X "$i<DescriptorFileName>$(Esc-Xml $descriptor)</DescriptorFileName>"
|
||||
|
||||
$reuseSessions = Get-EnumProp "ReuseSessions" "reuseSessions" "DontUse"
|
||||
X "$i<ReuseSessions>$reuseSessions</ReuseSessions>"
|
||||
@@ -3952,7 +3984,10 @@ function Emit-Operation {
|
||||
$nillable = "false"
|
||||
$transactioned = "false"
|
||||
$handler = $opName
|
||||
$params = @{}
|
||||
$opComment = ""
|
||||
$dataLock = "Managed" # у всех 192 операций корпуса
|
||||
# [ordered]: порядок параметров — как в DSL (он же порядок исходного XML).
|
||||
$params = [ordered]@{}
|
||||
|
||||
if ($opDef -is [string]) {
|
||||
$returnType = "$opDef"
|
||||
@@ -3961,6 +3996,10 @@ function Emit-Operation {
|
||||
if ($opDef.nillable -eq $true) { $nillable = "true" }
|
||||
if ($opDef.transactioned -eq $true) { $transactioned = "true" }
|
||||
if ($opDef.handler) { $handler = "$($opDef.handler)" }
|
||||
if ($opDef.procedureName) { $handler = "$($opDef.procedureName)" }
|
||||
if ($opDef.synonym) { $opSynonym = "$($opDef.synonym)" }
|
||||
if ($opDef.comment) { $opComment = "$($opDef.comment)" }
|
||||
if ($opDef.dataLockControlMode) { $dataLock = "$($opDef.dataLockControlMode)" }
|
||||
if ($opDef.parameters) {
|
||||
$opDef.parameters.PSObject.Properties | ForEach-Object {
|
||||
$params[$_.Name] = $_.Value
|
||||
@@ -3972,11 +4011,12 @@ function Emit-Operation {
|
||||
X "$indent`t<Properties>"
|
||||
X "$indent`t`t<Name>$(Esc-Xml $opName)</Name>"
|
||||
Emit-MLText "$indent`t`t" "Synonym" $opSynonym
|
||||
X "$indent`t`t<Comment/>"
|
||||
X "$indent`t`t<XDTOReturningValueType>$returnType</XDTOReturningValueType>"
|
||||
if ($opComment) { X "$indent`t`t<Comment>$(Esc-Xml $opComment)</Comment>" } else { X "$indent`t`t<Comment/>" }
|
||||
Emit-XDTOType "$indent`t`t" "XDTOReturningValueType" $returnType
|
||||
X "$indent`t`t<Nillable>$nillable</Nillable>"
|
||||
X "$indent`t`t<Transactioned>$transactioned</Transactioned>"
|
||||
X "$indent`t`t<ProcedureName>$(Esc-Xml $handler)</ProcedureName>"
|
||||
X "$indent`t`t<DataLockControlMode>$dataLock</DataLockControlMode>"
|
||||
X "$indent`t</Properties>"
|
||||
|
||||
if ($params.Count -gt 0) {
|
||||
@@ -3988,6 +4028,7 @@ function Emit-Operation {
|
||||
$paramType = "xs:string"
|
||||
$paramNillable = "true"
|
||||
$paramDir = "In"
|
||||
$paramComment = ""
|
||||
|
||||
if ($paramDef -is [string]) {
|
||||
$paramType = "$paramDef"
|
||||
@@ -3995,13 +4036,16 @@ function Emit-Operation {
|
||||
if ($paramDef.type) { $paramType = "$($paramDef.type)" }
|
||||
if ($paramDef.nillable -eq $false) { $paramNillable = "false" }
|
||||
if ($paramDef.direction) { $paramDir = "$($paramDef.direction)" }
|
||||
if ($paramDef.synonym) { $paramSynonym = "$($paramDef.synonym)" }
|
||||
if ($paramDef.comment) { $paramComment = "$($paramDef.comment)" }
|
||||
}
|
||||
|
||||
X "$indent`t`t<Parameter uuid=`"$paramUuid`">"
|
||||
X "$indent`t`t`t<Properties>"
|
||||
X "$indent`t`t`t`t<Name>$(Esc-Xml $paramName)</Name>"
|
||||
Emit-MLText "$indent`t`t`t`t" "Synonym" $paramSynonym
|
||||
X "$indent`t`t`t`t<XDTOValueType>$paramType</XDTOValueType>"
|
||||
if ($paramComment) { X "$indent`t`t`t`t<Comment>$(Esc-Xml $paramComment)</Comment>" } else { X "$indent`t`t`t`t<Comment/>" }
|
||||
Emit-XDTOType "$indent`t`t`t`t" "XDTOValueType" $paramType
|
||||
X "$indent`t`t`t`t<Nillable>$paramNillable</Nillable>"
|
||||
X "$indent`t`t`t`t<TransferDirection>$paramDir</TransferDirection>"
|
||||
X "$indent`t`t`t</Properties>"
|
||||
@@ -4408,7 +4452,8 @@ if ($objType -eq "HTTPService") {
|
||||
|
||||
# --- WebService: Operations ---
|
||||
if ($objType -eq "WebService") {
|
||||
$operations = @{}
|
||||
# [ordered]: порядок операций — как в DSL (он же порядок исходного XML).
|
||||
$operations = [ordered]@{}
|
||||
if ($def.operations) {
|
||||
$def.operations.PSObject.Properties | ForEach-Object {
|
||||
$operations[$_.Name] = $_.Value
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# meta-compile v1.71 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.72 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -3737,18 +3737,44 @@ def emit_http_service_properties(indent):
|
||||
session_max_age = str(defn['sessionMaxAge']) if defn.get('sessionMaxAge') is not None else '20'
|
||||
X(f'{i}<SessionMaxAge>{session_max_age}</SessionMaxAge>')
|
||||
|
||||
def emit_xdto_type(indent, tag, value):
|
||||
"""XDTO-тип значения: "{uri}Local" (нотация Кларка) -> тег с ЛОКАЛЬНЫМ объявлением xmlns,
|
||||
как пишет платформа. Простой "xs:string" — как есть."""
|
||||
m = re.match(r'^\{([^}]+)\}(.+)$', value or '')
|
||||
if m:
|
||||
X(f'{indent}<{tag} xmlns:d1p1="{esc_xml(m.group(1))}">d1p1:{esc_xml(m.group(2))}</{tag}>')
|
||||
else:
|
||||
X(f'{indent}<{tag}>{esc_xml(value)}</{tag}>')
|
||||
|
||||
|
||||
def emit_web_service_properties(indent):
|
||||
i = indent
|
||||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||||
emit_mltext(i, 'Synonym', synonym)
|
||||
X(f'{i}<Comment/>')
|
||||
X(f'{i}<Comment>{esc_xml(str(defn["comment"]))}</Comment>' if defn.get('comment') else f'{i}<Comment/>')
|
||||
namespace = str(defn['namespace']) if defn.get('namespace') else ''
|
||||
X(f'{i}<Namespace>{esc_xml(namespace)}</Namespace>')
|
||||
xdto_packages = str(defn['xdtoPackages']) if defn.get('xdtoPackages') else ''
|
||||
if xdto_packages:
|
||||
X(f'{i}<XDTOPackages>{xdto_packages}</XDTOPackages>')
|
||||
# XDTOPackages — СПИСОК элементов: ссылка на пакет конфигурации (xr:MDObjectRef) либо URI
|
||||
# внешнего пространства имён (xs:string). Presentation пуст, CheckState 0 (корпус: 19/19).
|
||||
pkgs = defn.get('xdtoPackages') or []
|
||||
if not isinstance(pkgs, list):
|
||||
pkgs = [pkgs]
|
||||
if pkgs:
|
||||
X(f'{i}<XDTOPackages>')
|
||||
for p in pkgs:
|
||||
pv = str(p)
|
||||
xt = 'xr:MDObjectRef' if pv.startswith('XDTOPackage.') else 'xs:string'
|
||||
X(f'{i}\t<xr:Item>')
|
||||
X(f'{i}\t\t<xr:Presentation/>')
|
||||
X(f'{i}\t\t<xr:CheckState>0</xr:CheckState>')
|
||||
X(f'{i}\t\t<xr:Value xsi:type="{xt}">{esc_xml(pv)}</xr:Value>')
|
||||
X(f'{i}\t</xr:Item>')
|
||||
X(f'{i}</XDTOPackages>')
|
||||
else:
|
||||
X(f'{i}<XDTOPackages/>')
|
||||
# Имя файла дескриптора не выводится из имени сервиса (DMILService -> dmil.1cws) — только дефолт.
|
||||
descriptor = str(defn['descriptorFileName']) if defn.get('descriptorFileName') else f'{obj_name}.1cws'
|
||||
X(f'{i}<DescriptorFileName>{esc_xml(descriptor)}</DescriptorFileName>')
|
||||
reuse_sessions = get_enum_prop('ReuseSessions', 'reuseSessions', 'DontUse')
|
||||
X(f'{i}<ReuseSessions>{reuse_sessions}</ReuseSessions>')
|
||||
session_max_age = str(defn['sessionMaxAge']) if defn.get('sessionMaxAge') is not None else '20'
|
||||
@@ -3858,6 +3884,8 @@ def emit_operation(indent, op_name, op_def):
|
||||
nillable = 'false'
|
||||
transactioned = 'false'
|
||||
handler = op_name
|
||||
op_comment = ''
|
||||
data_lock = 'Managed' # у всех 192 операций корпуса
|
||||
params = {}
|
||||
if isinstance(op_def, str):
|
||||
return_type = op_def
|
||||
@@ -3870,6 +3898,14 @@ def emit_operation(indent, op_name, op_def):
|
||||
transactioned = 'true'
|
||||
if op_def.get('handler'):
|
||||
handler = str(op_def['handler'])
|
||||
if op_def.get('procedureName'):
|
||||
handler = str(op_def['procedureName'])
|
||||
if op_def.get('synonym'):
|
||||
op_synonym = str(op_def['synonym'])
|
||||
if op_def.get('comment'):
|
||||
op_comment = str(op_def['comment'])
|
||||
if op_def.get('dataLockControlMode'):
|
||||
data_lock = str(op_def['dataLockControlMode'])
|
||||
if op_def.get('parameters'):
|
||||
for k, v in op_def['parameters'].items():
|
||||
params[k] = v
|
||||
@@ -3877,20 +3913,23 @@ def emit_operation(indent, op_name, op_def):
|
||||
X(f'{indent}\t<Properties>')
|
||||
X(f'{indent}\t\t<Name>{esc_xml(op_name)}</Name>')
|
||||
emit_mltext(f'{indent}\t\t', 'Synonym', op_synonym)
|
||||
X(f'{indent}\t\t<Comment/>')
|
||||
X(f'{indent}\t\t<XDTOReturningValueType>{return_type}</XDTOReturningValueType>')
|
||||
X(f'{indent}\t\t<Comment>{esc_xml(op_comment)}</Comment>' if op_comment else f'{indent}\t\t<Comment/>')
|
||||
emit_xdto_type(f'{indent}\t\t', 'XDTOReturningValueType', return_type)
|
||||
X(f'{indent}\t\t<Nillable>{nillable}</Nillable>')
|
||||
X(f'{indent}\t\t<Transactioned>{transactioned}</Transactioned>')
|
||||
X(f'{indent}\t\t<ProcedureName>{esc_xml(handler)}</ProcedureName>')
|
||||
X(f'{indent}\t\t<DataLockControlMode>{data_lock}</DataLockControlMode>')
|
||||
X(f'{indent}\t</Properties>')
|
||||
if params:
|
||||
X(f'{indent}\t<ChildObjects>')
|
||||
for param_name, param_def in sorted(params.items()):
|
||||
# Порядок — как в DSL (он же порядок исходного XML), а не отсортированный.
|
||||
for param_name, param_def in params.items():
|
||||
param_uuid = new_uuid()
|
||||
param_synonym = split_camel_case(param_name)
|
||||
param_type = 'xs:string'
|
||||
param_nillable = 'true'
|
||||
param_dir = 'In'
|
||||
param_comment = ''
|
||||
if isinstance(param_def, str):
|
||||
param_type = param_def
|
||||
else:
|
||||
@@ -3900,11 +3939,16 @@ def emit_operation(indent, op_name, op_def):
|
||||
param_nillable = 'false'
|
||||
if param_def.get('direction'):
|
||||
param_dir = str(param_def['direction'])
|
||||
if param_def.get('synonym'):
|
||||
param_synonym = str(param_def['synonym'])
|
||||
if param_def.get('comment'):
|
||||
param_comment = str(param_def['comment'])
|
||||
X(f'{indent}\t\t<Parameter uuid="{param_uuid}">')
|
||||
X(f'{indent}\t\t\t<Properties>')
|
||||
X(f'{indent}\t\t\t\t<Name>{esc_xml(param_name)}</Name>')
|
||||
emit_mltext(f'{indent}\t\t\t\t', 'Synonym', param_synonym)
|
||||
X(f'{indent}\t\t\t\t<XDTOValueType>{param_type}</XDTOValueType>')
|
||||
X(f'{indent}\t\t\t\t<Comment>{esc_xml(param_comment)}</Comment>' if param_comment else f'{indent}\t\t\t\t<Comment/>')
|
||||
emit_xdto_type(f'{indent}\t\t\t\t', 'XDTOValueType', param_type)
|
||||
X(f'{indent}\t\t\t\t<Nillable>{param_nillable}</Nillable>')
|
||||
X(f'{indent}\t\t\t\t<TransferDirection>{param_dir}</TransferDirection>')
|
||||
X(f'{indent}\t\t\t</Properties>')
|
||||
@@ -4297,7 +4341,8 @@ if obj_type == 'WebService':
|
||||
if operations:
|
||||
has_children = True
|
||||
X('\t\t<ChildObjects>')
|
||||
for op_name in sorted(op_order):
|
||||
# Порядок — как в DSL (он же порядок исходного XML), а не отсортированный.
|
||||
for op_name in op_order:
|
||||
emit_operation('\t\t\t', op_name, operations[op_name])
|
||||
X('\t\t</ChildObjects>')
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user