Files
svc-api-x/v1/resources/instance.cfc
T
2025-12-09 14:42:30 +03:00

522 lines
29 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<cfcomponent extends="taffy.core.resource" taffy:uri="/instances/{instanceUid}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<cffunction name="get" hint="Экземпляр сервиса"><!--- *** TODO проверка принадлежности тенанту --->
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset var local={}/>
<cfquery name="local.qInstance" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
<m:field>e.instance_uid::text as instance_uid</m:field>
<m:field>e.display_name</m:field>
<m:field>e.service_id</m:field>
<m:field>e.descr</m:field>
<m:field>e.specification_item_id</m:field>
<m:field formatter=#request.castToBool#>e.is_auxiliary</m:field>
<m:field>to_char(e.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_created</m:field><!--- to_json(e.dt_created)##>>'{}' --->
<m:field>to_char(e.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_updated</m:field>
<m:field>d.contract_id</m:field>
<m:field>i.specification_id</m:field>
<m:field>s.specification</m:field>
<m:field>i.quantity</m:field>
<m:field>i.price</m:field><!--- *** не цена, а скидка (Считая ссылку на базовый тариф атрибутом сервиса) --->
<m:field>v.svc</m:field>
<m:field>v.code</m:field>
<m:field>v.man
</m:field>
<m:field>e.updater_id</m:field>
<m:field>u.login as updater_login</m:field>
<m:field>u.shortname as updater_shortname</m:field>
<m:field>cast(st.instance_data->>'params' as json)->>'resourceRealm' as resource_realm</m:field>
<m:field formatter=#request.castToBool#>coalesce((io.dt_start IS NOT NULL AND io.dt_finish IS NULL AND io.submit_result='201'),false) as operation_is_in_progress</m:field>
<m:field formatter=#request.castToBool#>coalesce((io.dt_start IS NULL AND io.submit_result='201'),false) as operation_is_pending</m:field>
<m:field>case
when io.operation IN ('delete', 'suspend') AND io.is_successful then 0 /*null*/
when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP))
else 0 end
as uptime</m:field>
<m:field>(case
when st.instance_uid IS NULL then 'not created'
when st.is_deleted = 'true' then 'deleted'
when st.is_suspended = 'true' then 'suspended'
/*when coalesce(st.is_deleted,'false') = 'false' AND coalesce(st.is_suspended, 'false') = 'false' then 'running'*/
when coalesce(st.is_deleted,'false') = 'false' AND coalesce(st.is_suspended, 'false') = 'false' then 'running'
when io.instance_uid IS NULL then 'not configured'
else null end
) as explained_status
</m:field>
<m:field>(select count(distinct r.resource_realm)
from resource_realm r
join resource_realm_access a on (
r.resource_realm_id=a.resource_realm_id
AND (a.contract_id IN (
select contract_id
from contract d
where d.contragent_id=k.contragent_id AND NOT d.is_closed
) OR a.contract_id=0) /*0 means access to any contract*/
AND a.is_enabled)
where r.resource_realm_type_id=v.resource_realm_type_id) as resource_realm_cnt
</m:field>
<!--- <m:field>st.version</m:field> --->
</m:field_set>
from instance e
left outer join specification_item i on (e.specification_item_id=i.specification_item_id)
left outer join specification s on (i.specification_id=s.specification_id)
left outer join contract d on (s.contract_id=d.contract_id)
left outer join contragent k on (d.contragent_id=k.contragent_id)
left outer join svc v on (e.service_id=v.svc_id)
left outer join usr u on (e.updater_id=u.usr_id)
left outer join (select ist.instance_state_uid, ist.instance_uid, ist.version, ist.dt_state, ist.is_test
,(ist.instance_data->>'isSuspended') as is_suspended
,(ist.instance_data->>'isDeleted') as is_deleted
,ist.instance_data
from instance_state ist join (select instance_uid, max(version) as version from instance_state group by 1) lastv
on (ist.instance_uid=lastv.instance_uid AND ist.version=lastv.version) ) st
on (e.instance_uid=st.instance_uid)
left outer join (select o.instance_operation_uid, o.instance_uid, o.operation, o.dt_submit, o.submit_result, o.dt_start, o.dt_finish, o.is_successful
from instance_operation o join (select instance_uid, max(dt_submit) as dt_submit from instance_operation group by 1) lastv
on (o.instance_uid=lastv.instance_uid AND o.dt_submit=lastv.dt_submit) ) io
on (e.instance_uid=io.instance_uid)
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
AND s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/><!--- access protection --->
</cfquery> <!--- *** правильнее при попытке доступа к чужому инстансу (НСД) выбрасывать 403, а тут будет 404, но так тоже делают, например, в gitea --->
<cfif local.qInstance.recordCount EQ 0>
<cfreturn representationOf("Instance not found or no permission to view").withStatus(404)/>
</cfif>
<!--- instanceData?.suspended
instanceData?.out?.monitoring?.allDashboards
instanceData?.params
instanceData?.out --->
<cfquery name="local.qCurrentState" result="local.result">
select
<m:field_set titleMapOut="local.currentStateTitleMap" lengthOut="local.fieldCount">
<m:field>st.instance_state_uid::text as instance_state_uid</m:field>
<m:field>st.version</m:field>
<m:field>st.creator_id</m:field>
<m:field>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>st.instance_operation_uid::text as instance_operation_uid</m:field>
<!--- <m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data::text as instance_data</m:field> --->
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'out' as out</m:field>
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'params' as params</m:field>
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'vault' as vault</m:field>
<m:field formatter=#function(x){return (x GT 0)}#>(st.instance_data->>'isDeleted')::boolean as is_deleted</m:field>
<m:field formatter=#function(x){return (x GT 0)}#>(st.instance_data->>'isSuspended')::boolean as is_suspended</m:field>
</m:field_set>
from instance_state st
where st.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstance.instance_uid#" null=#!isValid('guid',local.qInstance.instance_uid)#/>
order by st.version desc
limit 1
</cfquery><!--- берем local.qInstance.instance_uid ради изоляции (access protection) --->
<cfset local.isCreated = (local.qCurrentState.recordCount GT 0)/>
<cfset local.isDeleted = (local.qCurrentState.is_deleted GT 0)/>
<!--- <cfdump var=#local.qInstance#/><cfabort/> --->
<!--- операция, создавшая текущее состояние --->
<!--- *** странноватый селект --->
<cfquery name="local.qOperation" result="local.result">
select
<m:field_set titleMapOut="local.operationTitleMap" lengthOut="local.fieldCount">
<m:field>o.instance_operation_uid::text as instance_operation_uid</m:field>
<m:field>status.instance_state_uid::text as instance_state_uid</m:field>
<m:field>status.version as state_version</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>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>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>extract('epoch' from coalesce(o.dt_finish,CURRENT_TIMESTAMP)- o.dt_start) as duration</m:field>
<m:field>round(extract('epoch' from CURRENT_TIMESTAMP - coalesce(o.dt_finish,CURRENT_TIMESTAMP)),1) as seconds_passed</m:field>
<m:field formatter=#request.castToBool#>o.is_successful</m:field>
<m:field title="dt_created">to_char(o.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
<m:field>o.error_log</m:field>
<m:field>u.login as updater_login</m:field>
<m:field>u.shortname as updater_shortname</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
left outer join usr u on (o.updater_id=u.usr_id)
left outer join resource_realm r on o.resource_realm_id=r.resource_realm_id
left outer join (select instance_operation_uid, max(version) as version from instance_state
group by instance_operation_uid) sts on (o.instance_operation_uid=sts.instance_operation_uid)
left outer join instance_state status on (sts.instance_operation_uid=status.instance_operation_uid AND sts.version=status.version)
where o.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstance.instance_uid#" null=#!isValid('guid',local.qInstance.instance_uid)#/>
--order by status.version desc
order by o.dt_created desc
</cfquery>
<!--- *** А тут у нас не получится, что только операции с состояниями? Или в неправильном порядке? --->
<!--- ?Добавить параметры к операции --->
<!--- Разобраться в отличии текущей от последней. Может, нужна только одна? --->
<!--- <d:field title="Операция">(select operation from instance_state st join instance_operation io on (st.instance_operation_uid=io.instance_operation_uid) where st.instance_uid=e.instance_uid order by version desc limit 1) as operation</d:field>
<!--- текущая операция. Она еще не создала стейта и не завершена --->
<d:field title="Текущая операция">(select io.operation from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as current_operation</d:field> --->
<!--- *** Отработать блокировку --->
<cfquery name="local.qAvailableOperation" result="local.result">
select
<m:field_set titleMapOut="local.availableOperationTitleMap" lengthOut="local.fieldCount">
<m:field>so.svc_operation_id</m:field>
<m:field>so.operation</m:field>
<m:field>
(select instance_operation_uid::text
from instance_operation io
where io.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qInstance.instance_uid#" null=#!isValid('guid',local.qInstance.instance_uid)#/>
AND io.operation=so.operation AND io.dt_submit IS NULL
order by io.dt_created desc
limit 1
)as instance_operation_uid
</m:field>
</m:field_set>
from svc_operation so
where so.svc_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#local.qInstance.service_id#" null=#!isValid('integer',local.qInstance.service_id)#/>
<cfif local.isCreated>
AND NOT so.operation='create'
<cfelse>
AND so.operation='create'
</cfif>
<cfif local.isDeleted>
AND 1=0
</cfif>
order by so.svc_operation_id
</cfquery>
<!--- Проблема зависимостей. Чтобы их увидеть, нам нужно состояние, а его нет в реляционном виде.
Значит, придется парсить стейт
но чтобы получить все сервисы, которые зависят от данного, придется им отпарсить все стейты, что нереально
Значит, придется заняться нормализаций стейта (приведением в реляционный вид) - впрочем, ничего сильно сложного в этом нет
А есть смысл делать это в одну строну? наверно, есть - чтобы не забираться в операцию
--->
<!--- *** здесь уместна параноидальная проверка прав --->
<cfquery name="local.qDependency">
select
<m:field_set titleMapOut="local.dependencyTitleMap" lengthOut="local.fieldCount">
<m:field>p.param</m:field>
<m:field>sop.descr</m:field>
<m:field>s.svc_id</m:field>
<m:field>s.svc</m:field>
<m:field>s.code</m:field>
<m:field>sop.label</m:field>
<m:field>p.param_value as uid</m:field>
<m:field>r.display_name</m:field>
<m:field>r.is_auxiliary</m:field>
<m:field>c.contract_id</m:field>
<m:field>c.contract</m:field>
<m:field>to_char(c.dt_contract, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_contract</m:field>
<m:field>k.external_code</m:field>
</m:field_set>
from instance_cfs_param p
left outer join instance_operation_cfs_param iop on (p.instance_operation_cfs_param_uid=iop.instance_operation_cfs_param_uid)
left outer join instance_operation o on (iop.instance_operation_uid=o.instance_operation_uid)
left outer join instance e on (o.instance_uid=e.instance_uid)
left outer join svc_operation so on (o.operation=so.operation AND e.service_id=so.svc_id)
left outer join svc_operation_cfs_param sop on (so.svc_operation_id=sop.svc_operation_id AND p.param=sop.svc_operation_cfs_param)
left outer join svc s on (sop.ref_svc_id=s.svc_id)
join instance r on (p.param_value=r.instance_uid::text)
left outer join specification_item si on (r.specification_item_id=si.specification_item_id)
left outer join specification sp on (si.specification_id=sp.specification_id)
left outer join contract c on (sp.contract_id=c.contract_id)
left outer join contragent k on (c.contragent_id=k.contragent_id)
where p.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#"/>
AND sop.ref_svc_id > 0
order by sop.sort, p.param
</cfquery>
<!--- *** здесь уместна параноидальная проверка прав
потому что можно сослаться на чужой инстанс и увидеть какие-то лишние детали--->
<cfquery name="local.qDependentInstance">
select
<m:field_set titleMapOut="local.dependentInstanceTitleMap" lengthOut="local.fieldCount">
<m:field>p.param</m:field>
<m:field>sop.descr</m:field>
<m:field>s.svc_id</m:field>
<m:field>s.svc</m:field>
<m:field>s.code</m:field>
<m:field>sop.label</m:field>
<m:field>e.instance_uid::text as uid</m:field>
<m:field>e.display_name</m:field>
<m:field>e.is_auxiliary</m:field>
<m:field>c.contract_id</m:field>
<m:field>c.contract</m:field>
<m:field>to_char(c.dt_contract, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_contract</m:field>
<m:field>k.external_code</m:field>
</m:field_set>
from instance_cfs_param p
left outer join instance_operation_cfs_param iop on (p.instance_operation_cfs_param_uid=iop.instance_operation_cfs_param_uid)
left outer join instance_operation o on (iop.instance_operation_uid=o.instance_operation_uid)
left outer join instance e on (o.instance_uid=e.instance_uid)
left outer join svc s on (e.service_id=s.svc_id)
left outer join svc_operation so on (o.operation=so.operation AND e.service_id=so.svc_id)
left outer join svc_operation_cfs_param sop on (so.svc_operation_id=sop.svc_operation_id AND p.param=sop.svc_operation_cfs_param)
left outer join svc r on (sop.ref_svc_id=r.svc_id)
left outer join specification_item si on (e.specification_item_id=si.specification_item_id)
left outer join specification sp on (si.specification_id=sp.specification_id)
left outer join contract c on (sp.contract_id=c.contract_id)
left outer join contragent k on (c.contragent_id=k.contragent_id)
where
p.param_value=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.instanceUid#"/>
AND r.svc_id=(select i.service_id from instance i where i.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>)
</cfquery>
<!--- ---------------------------------------------------------------------------------- --->
<!--- маскируем секреты *** это не очень похоже на то же самое в списке, там мы просто удаляем параметры --->
<!--- *** подгоняем запрос под структуру из instance_operation_cfs_param --->
<!--- а мы тут упускаем, что может быть несколько операций create, как правило из-за сломавшихся. Но параметры мы берем из текущего стейта, и операцию возьмем оттуда же --->
<cfquery name="local.qCfsParam" result="local.result">
select
p.svc_operation_cfs_param
,p.svc_operation_cfs_param_id
,p.label
,p.descr
,p.is_sensitive
,p.data_type
,null as data_descriptor
,(select instance_operation_cfs_param_uid from instance_operation_cfs_param iop
join svc_operation_cfs_param sop on (iop.svc_operation_cfs_param_id=sop.svc_operation_cfs_param_id)
join instance_operation io on (iop.instance_operation_uid=io.instance_operation_uid)
where sop.svc_operation_cfs_param_id=p.svc_operation_cfs_param_id AND
io.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/> <!--- уже лишнее ---> AND
io.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#local.qCurrentState.instance_operation_uid#" null=#!isValid('guid',local.qCurrentState.instance_operation_uid)#/>
--limit 1 <!--- *** hotfix --->
)
from svc_operation_cfs_param p
join svc_operation o on (p.svc_operation_id=o.svc_operation_id)
join svc on (o.svc_id=svc.svc_id)
where svc.svc_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qInstance.service_id#" null=#!isValid('integer',qInstance.service_id)#/>
AND o.operation='create'
</cfquery>
<!--- *** сделано не так, как в instance_operation_cfs_param (а там тоже сделано так себе) --->
<cfset local.params = structNew("linked")/>
<cftry>
<cfset local.params=deserializeJson(local.qCurrentState.params)/>
<cfloop query="local.qCfsParam">
<cfset var paramName = local.qCfsParam.svc_operation_cfs_param/>
<cfif structKeyExists(local.params, paramName)>
<!--- маскируем секреты --->
<cfif local.qCfsParam.is_sensitive>
<cfset local.params[paramName]="********"/>
<cfelseif local.qCfsParam.data_type EQ "map" OR local.qCfsParam.data_type EQ "map-fixed" OR local.qCfsParam.data_type EQ "array-map-fixed">
<!--- Не разбираем вариант "sensitive map" --->
<!--- <cfset processMap(local.qCfsParam)/> ---><!--- query passed by reference --->
<!--- маскируем секреты в map --->
<!--- *** сделано ненадежно, с расчетом на структуру qCfsParam (ради этого ее пришлось дополнить) --->
<!--- что характерно, мы передаем запрос (с фокусом, установленным на текущую строку) по ссылке и прямо там ее патчим --->
<cfset CreateObject("component", "instance_operation_cfs_param").processMap(local.qCfsParam)/>
</cfif>
</cfif>
</cfloop>
<!--- грубо и цинично патчим запрос --->
<cfset local.qCurrentState.params = serializeJson(local.params)/>
<cfcatch type="ANY"><!--- *** <cfrethrow/>---></cfcatch>
</cftry>
<!--- ---------------------------------------------------------------------------------- --->
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.instance" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qInstance, this.helper.snake2camel
)/>
<!--- <cfset "out.instance.uptime"=(local.qOperation.is_successful GT 0) ? local.qOperation.seconds_passed : 0/> --->
<cfset "out.instance.isCreated"=local.isCreated/>
<cfset "out.instance.isDeleted"=local.qCurrentState.is_deleted GT 0/>
<!--- <cfset "out.instance.vaultData"=local.vaultData/> --->
<cfset "out.instance.state" = (local.qCurrentState.recordCount GT 0) ? this.helper.appendRecord(structNew("linked"), "", local.currentStateTitleMap, local.qCurrentState, this.helper.snake2camel) : {}/>
<cfset "out.instance.operations"=[]/>
<cfloop query=#local.qOperation#>
<cfset arrayAppend(out.instance.operations, this.helper.appendRecord(structNew("linked"), "", local.operationTitleMap, local.qOperation, this.helper.snake2camel))/>
</cfloop>
<cfset "out.instance.availableOperations"=[]/>
<cfloop query=#local.qAvailableOperation#>
<cfset arrayAppend(out.instance.availableOperations, this.helper.appendRecord(structNew("linked"), "", local.availableOperationTitleMap, local.qAvailableOperation, this.helper.snake2camel))/>
</cfloop>
<!--- <cfset "out.instance.dependencies0"=#dependencies#/> --->
<cfset "out.instance.dependentInstances"=[]/>
<cfloop query=#local.qDependentInstance#>
<cfset arrayAppend(out.instance.dependentInstances, this.helper.appendRecord(structNew("linked"), "", local.dependentInstanceTitleMap, local.qDependentInstance, this.helper.snake2camel))/>
</cfloop>
<cfset "out.instance.dependencies"=[]/>
<cfloop query=#local.qDependency#>
<cfset arrayAppend(out.instance.dependencies, this.helper.appendRecord(structNew("linked"), "", local.dependencyTitleMap, local.qDependency, this.helper.snake2camel))/>
</cfloop>
<!--- <cfset "out.instance.availableResourceRealms"=[]/>
<cfloop query=#local.qAvailableResourceRealm#>
<cfset arrayAppend(out.instance.availableResourceRealms, this.helper.appendRecord(structNew("linked"), "", local.realmTitleMap, local.qAvailableResourceRealm, this.helper.snake2camel))/>
</cfloop> --->
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<!--- <cfdump var=#local.qCurrentState#/>
<cfdump var=#out#/>
<cfabort/> --->
<cfreturn representationOf(out) />
</cffunction>
<cffunction name="patch">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cfargument name="displayName" type="string" required=false hint="type:string"/>
<!--- <cfargument name="resourceRealmId" type="string" required=false hint="type:integer"/> --->
<cfargument name="descr" type="string" required=false default="" hint="type:varchar (no cleanup here!)"/>
<!--- ****** проверка уникальности --->
<cftry>
<cfset checkDisplayName(arguments.displayName, arguments.usrId, arguments.instanceUid)/><!--- как идея - офорить как обертку, возвращающую то же значение. Но будет соблазн еще оттримить иначе пофиксить, а это уже избыточная функция и должно усложнить название --->
<cfset this.helper.keyExistsAndValid(arguments, "instanceUid", "guid")/>
<cfquery name="local.qSave">
update instance set
dt_updated=<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Now()#" />
,updater_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usrId#" />
<cfif structKeyExists(arguments,"displayName")>
,display_name=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.displayName#" />
</cfif>
<!--- <cfif structKeyExists(arguments,"displayName")>
,resource_realm_id=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.resourceRealmId#" />
</cfif> --->
<cfif structKeyExists(arguments,"descr")>
,descr=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.descr#" />
</cfif>
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" />;
--select @@rowcount as cnt;
</cfquery>
<!--- <cfif local.qSave.cnt EQ 0>
<cfreturn noData().withStatus(404, "Record Not Found") />
</cfif> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction>
<cffunction name="delete" hint="Удаление экземпляра сервиса по ключу. Допускается в статусе not configured">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cftry>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance e
join specification_item si on (e.specification_item_id=si.specification_item_id)
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
AND si.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
</cfquery>
<cfif local.qCheckAccess.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage(
"Instance not accessible or does not exist",
"Instance is not accessible for the current user (possibly does not belong to the default specification)")
).withStatus(404)/>
</cfif>
<!--- халява какая-то --->
<cfquery name="local.qCheckOperation" result="local.result">
select count(*) as cnt
from instance_operation io
where io.dt_finish IS NULL AND
io.submit_result like '2%' /*201 etc*/ AND
io.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
</cfquery>
<cfif local.qCheckOperation.cnt GT 0>
<cfreturn representationOf(this.helper.formatMessage(
"Operation already started", "Instance deletion disabled when operation is started")
).withStatus(422)/>
</cfif>
<cfquery name="local.qCheckState" result="local.result">
select count(*) as cnt
from instance_state st
where
st.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
</cfquery>
<cfif local.qCheckState.cnt GT 0>
<cfreturn representationOf(this.helper.formatMessage(
"Instance has a state", "Instance deletion disabled when having state")
).withStatus(422)/>
</cfif>
<!--- мы не включаем проверку доступа в саму операцию удаления, а выполняем отдельно --->
<cfquery name="local.qSave">
delete from instance_state
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>;
delete from instance_operation
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>;
delete from instance
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>;
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- delete --->
<!--- неудобно бегать из одного компонента в соседний при том, что разделение ответственности между ними получается довольно странное
Решил, что логичнее повесить проверки уникальности и генерацию дефолтов на коллекцию, тем более и связей так меньше --->
<cffunction name="checkDisplayName">
<cfargument name="displayName" required=true/>
<cfargument name="usrId" type="numeric" required=true/>
<cfargument name="instanceUid" type="guid" required=true/>
<cfset CreateObject("component", "instance_ls").checkDisplayName(arguments.displayName, arguments.usrId, arguments.instanceUid)/>
</cffunction>
</cfcomponent>