326 lines
19 KiB
Plaintext
326 lines
19 KiB
Plaintext
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperations/{instanceOperationUid}">
|
||
|
||
<cfsilent>
|
||
<cfimport prefix="m" taglib="../lib"/>
|
||
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
|
||
</cfsilent>
|
||
|
||
|
||
<cffunction name="get" hint="Операция над экземпляром сервиса"><!--- *** TODO проверка принадлежности тенанту --->
|
||
<cfargument name="instanceOperationUid" type="string" required=true hint="type:guid"/>
|
||
<!--- arguments.specificationId injected implicitly --->
|
||
|
||
<cftry>
|
||
<cfset this.helper.validateField(arguments, "instanceOperationUid", "guid")/>
|
||
<cfcatch type="invalidParamValue">
|
||
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<cfset var out={}/>
|
||
|
||
<cfquery name="local.qInstanceOperation" result="local.result">
|
||
select
|
||
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
|
||
<m:field>o.instance_operation_uid::text as instance_operation_uid</m:field>
|
||
<m:field>o.operation</m:field>
|
||
<!--- <m:field>o.resource_realm_id</m:field>
|
||
<m:field>r.resource_realm</m:field> --->
|
||
<m:field>o.instance_uid::text as instance_uid</m:field>
|
||
<m:field>to_char(o.dt_submit, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_submit</m:field>
|
||
<m:field>o.submit_result</m:field>
|
||
<m:field>o.error_log</m:field>
|
||
<m:field>o.note</m:field>
|
||
<m:field>to_char(o.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_start</m:field>
|
||
<m:field>to_char(o.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_finish</m:field>
|
||
<m:field title="Длит. сек">extract('epoch' from coalesce(o.dt_finish,CURRENT_TIMESTAMP)- o.dt_start) as duration</m:field>
|
||
<m:field formatter=#request.castToBool#>o.is_successful</m:field>
|
||
<m:field>e.display_name</m:field>
|
||
<m:field>e.service_id</m:field>
|
||
<m:field>e.specification_item_id</m:field>
|
||
<m:field>to_char(o.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
|
||
<m:field>to_char(o.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_updated</m:field>
|
||
<m:field>o.updater_id</m:field>
|
||
<m:field>u.login as updater_login</m:field>
|
||
<m:field>u.shortname as updater_shortname</m:field>
|
||
<m:field>v.svc</m:field>
|
||
<m:field>so.man</m:field>
|
||
<m:field>so.svc_operation_id</m:field>
|
||
<m:field formatter=#request.castToBool#>coalesce((o.dt_start IS NOT NULL AND o.dt_finish IS NULL AND o.submit_result='201'),false) as is_in_progress</m:field>
|
||
<m:field formatter=#request.castToBool#>coalesce((o.dt_start IS NULL AND o.submit_result='201'),false) as is_pending</m:field>
|
||
</m:field_set>
|
||
from instance_operation o
|
||
join instance e on (o.instance_uid=e.instance_uid)
|
||
join specification_item i on (e.specification_item_id=i.specification_item_id)
|
||
join specification s on (i.specification_id=s.specification_id)
|
||
join contract d on (s.contract_id=d.contract_id)
|
||
join contragent k on (d.contragent_id=k.contragent_id)
|
||
join svc v on (e.service_id=v.svc_id)
|
||
join svc_operation so on (v.svc_id=so.svc_id AND o.operation=so.operation)
|
||
<!--- left outer join resource_realm r on (o.resource_realm_id=r.resource_realm_id) --->
|
||
left outer join usr u on (o.updater_id=u.usr_id)
|
||
where <!--- u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.usrId#/> AND--->
|
||
o.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationUid#" null=#!isValid('guid',arguments.instanceOperationUid)#/>
|
||
AND s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
|
||
</cfquery>
|
||
|
||
<!--- <cfdump var=#local.qInstanceOperation#/><cfabort/> --->
|
||
|
||
<cfif local.qInstanceOperation.recordCount EQ 0>
|
||
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
|
||
</cfif>
|
||
|
||
<!--- Чтобы не нагружать второй селект проверкой принадлежности клиенту, мы берем ключ не из аргументов, а из первого запроса --->
|
||
<cfquery name="local.qStage" result="local.result">
|
||
select
|
||
<m:field_set titleMapOut="local.stageMap" lengthOut="local.fieldCount">
|
||
<m:field>ios.instance_operation_stage_uid::text as instance_operation_stage_uid</m:field>
|
||
<m:field>ios.stage</m:field>
|
||
<m:field formatter=#request.castToBool#>ios.is_successful</m:field>
|
||
<m:field>to_char(ios.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_start</m:field>
|
||
<m:field>to_char(ios.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_finish</m:field>
|
||
<m:field>round(extract('epoch' from coalesce(ios.dt_finish,CURRENT_TIMESTAMP)- ios.dt_start),1) as duration</m:field>
|
||
<m:field>ios.stage_msg</m:field>
|
||
</m:field_set>
|
||
from instance_operation_stage ios
|
||
where ios.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstanceOperation.instance_operation_uid#" null=#!isValid('guid',local.qInstanceOperation.instance_operation_uid)#/>
|
||
order by dt_start asc
|
||
</cfquery>
|
||
|
||
<cfquery name="local.qState" result="local.result">
|
||
select
|
||
<m:field_set titleMapOut="local.stateTitleMap" lengthOut="local.fieldCount">
|
||
<m:field title="instance_state_uid">st.instance_state_uid::text as instance_state_uid</m:field>
|
||
<m:field title="v.">st.version</m:field>
|
||
<m:field title="creator_id">st.creator_id</m:field>
|
||
<m:field title="dt_state">to_char(st.dt_state, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_state</m:field>
|
||
<m:field formatter=#request.castToBool#>st.is_test</m:field>
|
||
<m:field title="instance_operation_uid">st.instance_operation_uid::text as instance_operation_uid</m:field>
|
||
<m:field title="instance_data" formatter=#function(x){return (deserializeJson(x))}#>st.instance_data::text as instance_data</m:field>
|
||
</m:field_set>
|
||
from instance_state st
|
||
where st.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstanceOperation.instance_operation_uid#" null=#!isValid('guid',local.qInstanceOperation.instance_operation_uid)#/>
|
||
order by st.version desc limit 1
|
||
</cfquery>
|
||
|
||
<cfquery name="local.qCurrentState" result="local.result">
|
||
select
|
||
<m:field_set titleMapOut="local.stateTitleMap" lengthOut="local.fieldCount">
|
||
<m:field title="instance_state_uid">st.instance_state_uid::text as instance_state_uid</m:field>
|
||
<m:field title="v.">st.version</m:field>
|
||
<m:field title="creator_id">st.creator_id</m:field>
|
||
<m:field title="dt_state">to_char(st.dt_state, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_state</m:field>
|
||
<m:field formatter=#request.castToBool#>st.is_test</m:field>
|
||
<m:field title="instance_operation_uid">st.instance_operation_uid::text as instance_operation_uid</m:field>
|
||
<m:field title="instance_data" formatter=#function(x){return (deserializeJson(x))}#>st.instance_data::text as instance_data</m:field>
|
||
</m:field_set>
|
||
from instance_state st
|
||
where st.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstanceOperation.instance_uid#" null=#!isValid('guid',local.qInstanceOperation.instance_uid)#/>
|
||
order by st.version desc limit 1
|
||
</cfquery>
|
||
|
||
<cfset var currentState = {}/>
|
||
<cftry>
|
||
<cfset currentState = deserializeJson(local.qCurrentState.instance_data)/>
|
||
<cfcatch type="ANY">
|
||
<!--- тут надо логировать ошибку ---><cfdump var=#cfcatch#/><cfabort/>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<cfquery name="local.qCfsParam" result="local.result">
|
||
select
|
||
<m:field_set titleMapOut="local.cfsParamTitleMap" lengthOut="fieldCount">
|
||
<m:field title="instance_operation_cfs_param_uid">iop.instance_operation_cfs_param_uid::text as instance_operation_cfs_param_uid</m:field>
|
||
<!--- <m:field title="instance_operation_uid">instance_operation_uid::text as instance_operation_uid</m:field> --->
|
||
<m:field>sop.svc_operation_cfs_param_id</m:field>
|
||
<!--- <m:field>iop.param_value</m:field> --->
|
||
<m:field>case when sop.is_sensitive then '********' else iop.param_value end as param_value </m:field>
|
||
<m:field>to_char(iop.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
|
||
<m:field>iop.creator_id</m:field>
|
||
<m:field>sop.svc_operation_cfs_param</m:field>
|
||
<m:field>sop.label</m:field>
|
||
<m:field>sop.data_type</m:field>
|
||
<m:field>null as data_descriptor</m:field>
|
||
<m:field formatter=#(x)=>((isArray(x) OR isEmpty(x))? x : listToArray(x))#>sop.value_list</m:field>
|
||
<m:field>sop.ref_svc_id</m:field>
|
||
|
||
<m:field>sop.func</m:field>
|
||
<m:field>sop.nested_ref</m:field>
|
||
<m:field>sop.state_path</m:field>
|
||
<m:field>sop.expression</m:field>
|
||
<m:field>sop.depends_on_cfs_params</m:field>
|
||
|
||
<m:field formatter=#request.castToBool#>sop.is_required</m:field>
|
||
<m:field>sop.default_value</m:field>
|
||
<m:field>sop.regex</m:field>
|
||
<m:field>sop.unique_scope</m:field>
|
||
<m:field>sop.maxlength</m:field>
|
||
<m:field>sop.minlength</m:field>
|
||
<m:field>sop.maxvalue</m:field>
|
||
<m:field>sop.minvalue</m:field>
|
||
<m:field>sop.descr</m:field>
|
||
<m:field>sop.man</m:field>
|
||
<m:field>sop.sort</m:field>
|
||
<m:field>iop.note</m:field>
|
||
<m:field>sop.config::text as config</m:field>
|
||
<m:field>null as nested_ref_data</m:field><!--- placeholder for future data --->
|
||
|
||
|
||
|
||
<m:field formatter=#request.castToBool#>sop.is_sensitive</m:field>
|
||
</m:field_set>
|
||
from svc_operation_cfs_param sop
|
||
left outer join instance_operation_cfs_param iop
|
||
on (sop.svc_operation_cfs_param_id=iop.svc_operation_cfs_param_id
|
||
AND iop.instance_operation_uid = <cfqueryparam cfsqltype="cf_sql_other"
|
||
value="#local.qInstanceOperation.instance_operation_uid#"
|
||
null=#!isValid('guid',local.qInstanceOperation.instance_operation_uid)#/>
|
||
)
|
||
where sop.svc_operation_id = <cfqueryparam cfsqltype="cf_sql_integer"
|
||
value=#local.qInstanceOperation.svc_operation_id#
|
||
null=#!isValid('integer',local.qInstanceOperation.svc_operation_id)#/>
|
||
order by sop.sort, sop.svc_operation_cfs_param_id
|
||
</cfquery>
|
||
|
||
<cfloop query="local.qCfsParam">
|
||
<cfif len(func)><!--- приоритет отдается функции перед списком значений--->
|
||
<cfset var args = {
|
||
"functionName":"#func#",
|
||
"svcId":#local.qInstanceOperation.service_id#,
|
||
"contractId":#arguments.contractId#
|
||
}/> <!--- если появятся новые аргументы у новых функций, будем добавлять их сюда --->
|
||
<!--- <cfset local.qCfsParam.value_list=generateValueList(argumentCollection=#args#)/> --->
|
||
<cfinvoke component="instance_operation_cfs_param" method="generateValueList" argumentCollection=#args# returnVariable="local.qCfsParam.value_list"/>
|
||
<!--- можно перечислить именованные аргументы обычным порядком, но с коллекцией потенциально более гибко --->
|
||
<cfif len(trim(local.qCfsParam.value_list)) AND listLen(local.qCfsParam.value_list) EQ 1>
|
||
<cfset local.qCfsParam.default_value=local.qCfsParam.value_list/>
|
||
</cfif><!--- преобразование к массиву выполняется при финальном форматировании резалтсета --->
|
||
<cfelseif len(local.qCfsParam.nested_ref)>
|
||
<cfset var args = {
|
||
"functionName":"#local.qCfsParam.nested_ref#",
|
||
"config":"#local.qCfsParam.config#"
|
||
}/>
|
||
<cfinvoke component="instance_operation_cfs_param" method="generateClosure" argumentCollection=#args# returnVariable="local.fNestedRef"/>
|
||
<cfset local.qCfsParam.nested_ref_data = local.fNestedRef() />
|
||
<cfelseif len(local.qCfsParam.state_path) AND isStruct(currentState)><!--- deserializeJson может вернуть пустую строку вместо структуры,
|
||
по крайней мере, если получает поле пустого резалтсета --->
|
||
<cfset var args = {
|
||
"state":#currentState#,
|
||
"path":"#local.qCfsParam.state_path#"
|
||
}/>
|
||
<cfinvoke component="instance_operation_cfs_param"
|
||
method="getListFromState"
|
||
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 (len(local.qCfsParam.func) OR len(local.qCfsParam.expression) OR len(local.qCfsParam.state_path) OR len(local.qCfsParam.nested_ref))
|
||
AND len(local.qCfsParam.value_list) EQ 0>
|
||
<cfset local.qCfsParam.value_list = []/>
|
||
</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 --->
|
||
</cfloop>
|
||
|
||
|
||
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
|
||
|
||
<cfset "out.instanceOperation" = this.helper.appendRecord(
|
||
structNew("linked"), "", local.titleMap, local.qInstanceOperation, this.helper.snake2camel
|
||
)/>
|
||
|
||
<cfset "out.instanceOperation.cfsParams"=[]/>
|
||
<cfloop query=#local.qCfsParam#>
|
||
<cfset arrayAppend(out.instanceOperation.cfsParams, this.helper.appendRecord(structNew("linked"), "", local.cfsParamTitleMap, local.qCfsParam, this.helper.snake2camel))/>
|
||
</cfloop>
|
||
|
||
<cfset "out.instanceOperation.stages"=[]/>
|
||
<cfloop query=#local.qStage#>
|
||
<cfset arrayAppend(out.instanceOperation.stages, this.helper.appendRecord(structNew("linked"), "", local.stageMap, local.qStage, this.helper.snake2camel))/>
|
||
</cfloop>
|
||
|
||
<cfset "out.instanceOperation.state"=(local.qState.recordCount GT 0) ? this.helper.appendRecord(structNew("linked"), "", local.stateTitleMap, local.qState, this.helper.snake2camel) : {}/>
|
||
|
||
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
|
||
<cfreturn representationOf(out) />
|
||
</cffunction>
|
||
|
||
|
||
<!--- *** копия кода из deck-tool inst_op.cfc, коллекции статических методов операции инстанса --->
|
||
<cffunction name="validateCfsParams" returntype="string">
|
||
<cfargument name="instanceOperationUid" type="guid" required="true"/>
|
||
<!--- проверить CFS параметры в совокупности. Нужно, поскольку параметры не всегда могут быть проверены по одному,
|
||
пример - проверка уникальности парамера в рамках платформы, которая сама представлена параметром операции --->
|
||
<!--- пока сюда попали только две проверки уникальности --->
|
||
<cfset var local={}/>
|
||
<cfquery name="local.qSvcOperationParam">
|
||
select sop.svc_operation_cfs_param_id, sop.svc_operation_cfs_param, sop.unique_scope
|
||
from svc_operation_cfs_param sop
|
||
join instance_operation_cfs_param iop on (sop.svc_operation_cfs_param_id=iop.svc_operation_cfs_param_id)
|
||
where iop.instance_operation_uid = <cfqueryparam cfsqltype="CF_SQL_OTHER" value=#arguments.instanceOperationUid#/>
|
||
</cfquery>
|
||
<cfquery name="local.qInstance">
|
||
select io.instance_uid
|
||
from instance_operation io
|
||
join instance_operation_cfs_param iop on (io.instance_operation_uid=iop.instance_operation_uid)
|
||
where iop.instance_operation_uid = <cfqueryparam cfsqltype="CF_SQL_OTHER" value=#arguments.instanceOperationUid#/>
|
||
</cfquery>
|
||
|
||
<cfloop query="local.qSvcOperationParam">
|
||
<cfswitch expression=#local.qSvcOperationParam.unique_scope#>
|
||
<cfcase value="parent">
|
||
<!--- сверяем параметр операции с параметрами инстансов --->
|
||
<cfquery name="local.qUnique">
|
||
SELECT p.param_value, sp.svc_operation_cfs_param, re.display_name
|
||
FROM instance_operation o
|
||
JOIN instance_operation_cfs_param p on (o.instance_operation_uid=p.instance_operation_uid)
|
||
--JOIN svc s on (e.service_id=s.svc_id)
|
||
-- svc_operation_cfs_param нужен, чтобы убрать совпадение параметра с самим собой, это не является ошибкой
|
||
-- остается непонятным, будет ли работать проверка между create/modify, у них разные id, а в параметрах инстанса одно и то же
|
||
-- возможно, следует сравнивать просто по имени
|
||
--join instance_operation_cfs_param op on (p.instance_operation_cfs_param_uid=op.instance_operation_cfs_param_uid)
|
||
join svc_operation_cfs_param sp on (p.svc_operation_cfs_param_id=sp.svc_operation_cfs_param_id)
|
||
|
||
JOIN instance_operation_cfs_param p2 on (p.instance_operation_uid=p2.instance_operation_uid) -- sibling params
|
||
JOIN instance re on (p2.param_value=re.instance_uid::text /*не проверяем тип параметра - ссылка, не проверяем соответствие теп*/) -- referenced instance
|
||
-- down back
|
||
LEFT OUTER JOIN instance_cfs_param p3 on (re.instance_uid::text=p3.param_value /*добавить селективности*/) -- referencing params from sibling instance
|
||
LEFT OUTER JOIN instance_cfs_param p4 on (p3.instance_uid=p4.instance_uid AND sp.svc_operation_cfs_param=p4.param) -- same param names from sibling instance
|
||
|
||
WHERE p.param_value=p4.param_value
|
||
AND p.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value=#arguments.instanceOperationUid#/>
|
||
AND p.svc_operation_cfs_param_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#local.qSvcOperationParam.svc_operation_cfs_param_id#/>
|
||
AND p4.instance_uid <> <cfqueryparam cfsqltype="cf_sql_other" value=#qInstance.instance_uid# null=#!isValid("guid",qInstance.instance_uid)#/>
|
||
AND (select cast(st.instance_data->>'isDeleted' as boolean) from instance_state st where st.instance_uid=p4.instance_uid order by version desc limit 1) IS NOT TRUE
|
||
</cfquery>
|
||
<cfif qUnique.recordCount GT 0>
|
||
<cfreturn '#local.qUnique.svc_operation_cfs_param# = "#local.qUnique.param_value#" is not unique in the parent instance (#local.qUnique.display_name#) scope'/>
|
||
<!--- *** неточность: мы показываем только первую коллизию, нет полной уверенности, что рядом не окажется второй, хотя представить себе такую ситуацию достаточно сложно --->
|
||
</cfif>
|
||
<!--- <cfdump var=#qUnique#/><cfabort/> --->
|
||
</cfcase>
|
||
<cfdefaultcase></cfdefaultcase>
|
||
</cfswitch>
|
||
</cfloop>
|
||
<cfreturn ""/>
|
||
<!--- некрасиво, но просто - пустое сообщение об ошибке означает успешную проверку (изящнее не придумал) --->
|
||
|
||
</cffunction>
|
||
|
||
|
||
</cfcomponent> |