201 intermediate commit for state navigation

This commit is contained in:
2026-01-24 12:07:12 +03:00
parent d843620292
commit ee9331e6cd
11 changed files with 222 additions and 22 deletions
+1 -1
View File
@@ -57,7 +57,7 @@
//variables.framework.docs={};
variables.framework.docs.APIName="svc-api";
variables.framework.docs.APIVersion="0.199"; /*$ git config --global --unset user.password*/
variables.framework.docs.APIVersion="0.201"; /*$ git config --global --unset user.password*/
variables.framework.globalHeaders = structNew();
variables.framework.globalHeaders["Access-Control-Expose-Headers"] = "Location";
+40 -10
View File
@@ -35,6 +35,14 @@
<cfset var args=[]/>
<cfset parse(args, tokenize(arguments.expression),0)/>
<cfreturn computeExpression(args[1].function, args[1].args, arguments.context)/>
</cffunction>
<cffunction name="extractParams" returntype="any" access="public">
<cfargument name="expression" type="string"/>
<cfset var params=[]/>
<cfset parseForParams(params, tokenize(arguments.expression),0)/>
<cfreturn params/>
</cffunction>
<!---
@@ -115,6 +123,38 @@ expression parse-evaluate
</cfloop>
</cffunction>
<cffunction name="parseForParams" returntype="numeric" access="private"><!--- returns last token position --->
<cfargument name="args" type="array"/><!--- puts here parsed params --->
<cfargument name="tokens" type="array"/>
<cfargument name="positionBefore" type="numeric"/><!--- before first processed token, initially 0 --->
<cfset var i = arguments.positionBefore/>
<cfloop condition="++i LE arrayLen(arguments.tokens)">
<cfswitch expression=#tokenType(arguments.tokens[i])#>
<cfcase value="string">
<!--- skip ---><!--- <cfset arrayAppend(arguments.args,mid(arguments.tokens[i],2,len(arguments.tokens[i])-2))/>--->
</cfcase>
<cfcase value="variable">
<cfset arrayAppend( arguments.args, tokens[i] )/>
</cfcase>
<cfcase value="function">
<cfset var f = {"function"=arguments.tokens[i],"args"=[]}/>
<cfif i GE arrayLen(arguments.tokens)>
<cfthrow message="unexpected end of expression"/>
</cfif>
<cfif #tokenType(arguments.tokens[++i])# NEQ "("><!--- skip opening parenthesis ---><!--- *** possible out of range --->
<cfthrow message="function syntax incorrect" detail="function #arguments.tokens[i-1]#, token='#arguments.tokens[i]#' (#arrayToList(tokens,' ')#"/>
</cfif>
<cfset i = parseForParams(f.args, arguments.tokens, i)/>
<!--- <cfset arrayAppend(arguments.args,f)/> --->
</cfcase>
<cfcase value=")">
<cfreturn i/><!--- returns last token position --->
</cfcase>
</cfswitch>
</cfloop>
</cffunction>
<cffunction name="computeExpression" returntype="any" access="private"><!--- вычисление выражения снаружи внутрь --->
<cfargument name="function" type="string"/>
@@ -138,16 +178,6 @@ expression parse-evaluate
<cfargument name="functionName"/><!--- а ссылку на функцию передать не получится? (замыкание даже наверно не нужно) --->
<cfargument name="args" type="array"/>
<cfargument name="context" type="any"/>
<!--- <cfoutput>#arguments.functionName#</cfoutput> args<cfdump var=#arguments.args#/> --->
<!--- <cfswitch expression=#arguments.functionName#>
<cfcase value="concat"><cfreturn concat(argumentCollection=arguments.args)/></cfcase>
<cfcase value="getResourceRealmConfig"><cfreturn getResourceRealmConfig(argumentCollection=arguments.args)/></cfcase>
<cfcase value="getKeyListFromStruct"><cfreturn getKeyListFromStruct(argumentCollection=arguments.args)/></cfcase>
<cfcase value="getValue"><cfreturn getValue(argumentCollection=arguments.args)/></cfcase>
<cfdefaultcase>
<cfthrow message="Undefined Function" detail="#arguments.functionName#"/>
</cfdefaultcase>
</cfswitch> --->
<!--- <cfdump var=#arguments#/> --->
<cfset var obj = createObject("component", arguments.context.component).init(arguments.context.keys, arguments.context.params)/>
<!--- <cfdump var=#obj# abort=true/> --->
+14 -1
View File
@@ -160,6 +160,8 @@
<m:field>sop.config::text as config</m:field>
<m:field>null as nested_ref_data</m:field><!--- placeholder for future data --->
<m:field>sop.state_path</m:field>
<m:field>sop.depends_on_cfs_params</m:field>
<m:field>sop.expression</m:field>
<m:field formatter=#request.castToBool#>sop.is_sensitive</m:field>
</m:field_set>
from svc_operation_cfs_param sop
@@ -206,8 +208,19 @@
argumentCollection=#args#
returnVariable="local.qCfsParam.value_list"
/>
<cfelseif len(local.qCfsParam.expression) AND len(local.qCfsParam.instance_operation_cfs_param_uid)>
<cfset local.qCfsParam.value_list = createObject("component","lib.expression_parser")
.eval(
local.qCfsParam.expression,
{/*context*/
component:"resources.instance_operation_cfs_param",
keys:{"instanceOperationCfsParamUid"=#local.qCfsParam.instance_operation_cfs_param_uid#},
params:{}
}
)
/>
</cfif>
<!--- *** боюсь, где-то тут нужно будет делать вычисления субпараметров --->
<cfif data_type EQ "map" OR data_type EQ "map-fixed" OR data_type EQ "array-map-fixed">
<cfinvoke component="instance_operation_cfs_param" method="processMap" qParam=#local.qCfsParam#/>
</cfif><!---/map --->
+27 -4
View File
@@ -36,7 +36,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset this.instance_uid = "00000000-0000-0000-0000-000000000000"/><!--- *** костыль default=--->
<cfif structKeyExists(arguments.keys,"instanceOperationCfsParamUid")>
<cfset this.instance_operation_cfs_param_uid=arguments.keys.instanceOperationCfsParamUid/>
<cfset this.instance_operation_cfs_param_uid=arguments.keys.instanceOperationCfsParamUid/><!--- *** camelCase!!! --->
<cfquery name="local.qResolve">
select p.instance_operation_uid, o.instance_uid, p.svc_operation_cfs_param_id
from instance_operation_cfs_param p
@@ -274,7 +274,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
AND s.svc_operation_cfs_param = <cfqueryparam cfsqltype="cf_sql_varchar" value=#arguments.param#/>
</cfquery>
<cfif structKeyExists(arguments,"subparam")><!--- *** не изящно, но просто и понятно --->
<cfreturn deserializeJson(local.qCfsParam.param_value)[arguments.subparam]/>
<cfset var value = deserializeJson(local.qCfsParam.param_value)/>
<cfif isStruct(value) AND structKeyExists(value, arguments.subparam)>
<cfreturn value[arguments.subparam]/>
<cfelse>
<cfreturn ""/><!--- *** а надо выбросить 404 --->
</cfif>
<cfelse>
<cfreturn local.qCfsParam.param_value/>
</cfif>
@@ -628,6 +633,8 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
,sp.regex
,sp.sort
,sp.is_sensitive
,sp.depends_on_cfs_params
,sp.expression
--,sp.func
from svc_operation_cfs_subparam sp
where sp.svc_operation_cfs_param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.qParam.svc_operation_cfs_param_id#"/>
@@ -649,6 +656,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<!--- формируем дескриптор --->
<cfset var mapDescriptor=structNew("linked")/>
<cfset var parser=createObject("component","lib.expression_parser")/>
<cfloop query="local.qSubparam">
<cfset structInsert(mapDescriptor, local.qSubparam.svc_operation_cfs_subparam,
@@ -657,7 +665,19 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
"svcOperationCfsSubparam":"#local.qSubparam.svc_operation_cfs_subparam#",
"label":"#local.qSubparam.label#",
"dataType":"#local.qSubparam.data_type#",
"valueList":"#local.qSubparam.value_list#",
"valueList":'#(len(local.qSubparam.expression)?
(parser.eval(
qSubparam.expression,
{
component:"resources.instance_operation_cfs_param",
keys:{
instanceOperationCfsParamUid:"#toString(arguments.qParam.instance_operation_cfs_param_uid)#", /*note camelCase*/
svcOperationCfsParamId:"#toString(arguments.qParam.svc_operation_cfs_param_id)#" /*note camelCase*/
},
params:{}
}
))
: local.qSubparam.value_list)#',
"minlength":"#local.qSubparam.minlength#",
"maxlength":"#local.qSubparam.maxlength#",
"regex":"#local.qSubparam.regex#",
@@ -667,7 +687,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<!--- "func":"#local.qSubparam.func#", --->
"isRequired":#request.castToBool(local.qSubparam.is_required)#,
"isSensitive":#request.castToBool(local.qSubparam.is_sensitive)#,
"isModifiableDefinition":false
"isModifiableDefinition":false,
"dependsOnCfsParams":"#local.qSubparam.depends_on_cfs_params#",
"expression":"#local.qSubparam.expression#"
}, true
)
/>
@@ -31,7 +31,8 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
login={prefix="iop", type="string"},
creator_id={prefix="iop", type="integer"},
sort={prefix="sop", type="integer"},
is_sensitive={prefix="sop", type="boolean"}
is_sensitive={prefix="sop", type="boolean"},
depends_on_cfs_params={prefix="sop", type="string"}
<!--- ,
dt_updated={prefix="e", type="date"},
service_id={prefix="e", type="integer"},
@@ -91,6 +92,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<m:field>sop.default_value</m:field>
<m:field>sop.func</m:field>
<m:field>sop.man</m:field>
<m:field>sop.depends_on_cfs_params</m:field>
<m:field>null as data_descriptor</m:field>
<m:field>e.service_id</m:field>
</m:field_set>
+6 -3
View File
@@ -95,7 +95,7 @@
<m:field>p.config::text</m:field><!--- в таком виде конфиг доступен для кода, но не выводится в респонсе --->
<m:field>null as nested_ref_data</m:field>
<m:field>p.state_path</m:field>
<m:field>p.depends_on_params</m:field>
<m:field>p.depends_on_cfs_params</m:field>
<m:field>p.expression</m:field>
<m:field formatter=#request.castToBool#>(
select 1 from svc_operation_cfs_param m
@@ -160,6 +160,8 @@
,sp.label
,sp.data_type
,sp.value_list
,sp.depends_on_cfs_params
,sp.expression
,sp.is_required
,sp.descr
,sp.man
@@ -176,7 +178,7 @@
</cfquery>
<cfset var mapDescriptor=structNew("linked")/>
<!--- *** выдавать на фронт expression нам ни к чему, это временное явление --->
<cfloop query="local.qSubparam">
<cfset structInsert(mapDescriptor, local.qSubparam.svc_operation_cfs_subparam,
{
@@ -185,13 +187,14 @@
"label":"#local.qSubparam.label#",
"dataType":"#local.qSubparam.data_type#",
"valueList":"#local.qSubparam.value_list#",
"dependsOnCfsParams":"#local.qSubparam.depends_on_cfs_params#",
"expression":"#local.qSubparam.expression#",
"minlength":"#local.qSubparam.minlength#",
"maxlength":"#local.qSubparam.maxlength#",
"regex":"#local.qSubparam.regex#",
"man":"#local.qSubparam.man#",
"descr":"#local.qSubparam.descr#",
"defaultValue":"#local.qSubparam.default_value#",
<!--- "func":"#local.qSubparam.func#", --->
"isRequired":#request.castToBool(local.qSubparam.is_required)#,
"isSensitive":#request.castToBool(local.qSubparam.is_sensitive)#,
"isModifiableDefinition":false
+2
View File
@@ -59,6 +59,8 @@
<m:field>r.mgmt_api_url</m:field>
<m:field>p.resource_realm as parent</m:field>
<m:field>t.resource_realm_type</m:field>
<m:field>r.descr</m:field>
<m:field>r.man</m:field>
<m:field>to_char(r.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
<m:field>to_char(r.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_updated</m:field>
</m:field_set>
+1
View File
@@ -51,6 +51,7 @@
<m:field>p.descr</m:field>
<m:field>p.man</m:field>
<m:field>p.sort</m:field>
<m:field>p.depends_on_cfs_params</m:field>
<m:field formatter=#request.castToBool#>(
select 1 from svc_operation_cfs_param m
join svc_operation om on (m.svc_operation_id=om.svc_operation_id)
@@ -38,7 +38,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cftry>
<cfset this.helper.validateField(arguments, "svcOperationCfsParamId", "integer")/>
<cfset this.helper.validateField(arguments, "svcOperationCfsSubparamId", "integer", false)/>
<!--- <cfset this.helper.validateField(arguments, "svcOperationCfsSubparamId", "integer", false)/> --->
<cfset this.helper.validateField(arguments, "keys", "json")/> <!--- *** может, не лучшая идея передавать ключ в контексте --->
<cfset this.helper.validateField(arguments, "params", "json")/>
<cfcatch type="invalidParamValue">
@@ -0,0 +1,98 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/svcOperationCfsSubparams/compute/{svcOperationCfsSubparamId}" hint="вычисляет выражение в поле expression">
<!---
вычисляет выражение в поле expression
для этого нужен контекст - как минимум, сохраненные параметры, но это не работает при создании нового инстанса,
поэтому нужно инициализировать контекст и набивать его всей необходимой информацией
что особенно важно для зависимых параметров - нам нужно положить в контекст все параметры, от которых мы зависим
чтобы знать, какие параметры передавать - конструкцией для параметра предусмотрено поле depends_on_params (надо будет его сделать вычисляемым), а для субпараметра пока не
--->
<!--- Все методы неявно получают
arguments.usrId
arguments.contragentId
arguments.contractId
arguments.specificationId
arguments.requestArguments.usrId=usrCustomerInfo.usrId; //Integer!
arguments.requestArguments.contragentId=usrCustomerInfo.contragentId; //Integer
arguments.requestArguments.contractId=usrCustomerInfo.contractId; //Integer
arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //Integer
--->
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<cffunction name="get" hint="вычисление возможных значений cубпараметра CFS параметра операции, в текущем контексте">
<!--- <cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/> а вот ни фига--->
<!--- *** Важно: мы должны уметь посчитать параметр, если он не сохранен, то есть мы знаем его класс, а не инстанс --->
<!--- Вероятно, нам не очень нужно вычислять набор значений для инстанса параметра, мы всегда знаем класс --->
<!--- контест: --->
<cfargument name="svcOperationCfsSubparamId" type="string" required=false hint="type:integer"/>
<cfargument name="keys" type="string" default={} hint="type:json keys we depend on: of the param uid itself, or operation uid, instance uid, service operation cfs param - if not all entities intstantiated yet"/>
<cfargument name="params" type="string" default="{}" hint="type:json any posted parameters we depend on"/>
<cftry>
<cfset this.helper.validateField(arguments, "svcOperationCfsSubparamId", "integer")/>
<cfset this.helper.validateField(arguments, "keys", "json")/> <!--- *** может, не лучшая идея передавать ключ в контексте --->
<cfset this.helper.validateField(arguments, "params", "json")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfset local.expression = ""/> <!--- нет смысла выносить это в поля класса, оно нужно только локально --->
<cfset local.calculatedValueList = ""/>
<cfset local.keys = deserializeJson(arguments.keys)/>
<cfset local.params = deserializeJson(arguments.params)/>
<cfset local.keys.svcOperationCfsSubparamId = arguments.svcOperationCfsSubparamId/> <!--- *** сомнительное дело гонять аргументы туда-сюда --->
<cfquery name="local.qCfsSubparam" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="fieldCount">
<m:field>sop.data_type</m:field>
<m:field>sop.svc_operation_cfs_subparam</m:field>
<m:field>sop.expression</m:field>
</m:field_set>
from svc_operation_cfs_subparam sop
where sop.svc_operation_cfs_subparam_id
= <cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.svcOperationCfsSubparamId# null=#!isValid('integer',arguments.svcOperationCfsSubparamId)#/>
</cfquery>
<cfif local.qCfsSubparam.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Service Operation CFS Parameter Not Found")).withStatus(404)/>
</cfif>
<cfset local.expression=local.qCfsSubparam.expression/>
<!--- *** можно обратить внимание, что мы инстанциируем не субпараметр, а параметр (цинично пользуясь их функциональной идентичностью... но некрасиво) --->
<cfif len(local.expression)>
<cfset calculatedValueList=createObject("component","lib.expression_parser")
.eval(
local.expression,
{/*context*/
component:"resources.instance_operation_cfs_param", /*param, not subparam*/
keys:#local.keys#,
params:#local.params#
}
)
/>
<cfelse>
<cfreturn representationOf(this.helper.formatMessage("Expression Not Found")).withStatus(404)/>
</cfif>
<!--- Тут нам еще придется сформатировать лист в массив. Но обошлось довольно просто --->
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.values" = listToArray(local.calculatedValueList)/>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction><!--- get --->
</cfcomponent>