Files
svc-api-x/v1/resources/instance_operation_ls.cfc
T
2025-11-13 14:59:41 +03:00

280 lines
17 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="/instanceOperations">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<!---спецификация полей, пригодных для фильтрации (и сортировки)--->
<cfset this.fieldsSpec={
"instance_operation_uid"={prefix="o", type="guid"},
"instance_uid"={prefix="o", type="guid"},
"dt_created"={prefix="o", type="date"},
"dt_updated"={prefix="o", type="date"},
"creator_id"={prefix="o", type="integer"},
"updater_id"={prefix="o", type="integer"},
"dt_submit"={prefix="o", type="date"},
"submit_result"={prefix="o", type="string"},
"dt_start"={prefix="o", type="date"},
"dt_finish"={prefix="o", type="date"},
"specification_id"={prefix="s", type="integer"},
"specification_item_id"={prefix="i", type="integer"},
"contract_id"={prefix="inr", type="integer"},
"display_name"={prefix="e", type="string"},
"service_id"={prefix="e", type="integer"},
"svc"={prefix="v", type="string"}
}
/>
<!--- <cffunction name="post" hint="Создание новой операции с параметрами. Принимаются CFS параметры, генерируются RFS параметры. Запускается операция отдельным вызовом. До запуска CFS параметры могут корректироваться (а надо?). Запущенная операция навсегда становится Read Only (при желании можем реализовать ее клонирование). Можем реализовать корректировку параметров, но непонятно, зачем. Когда мы конфигурируем инстанс, на самом деле мы задаем CFS параметры операции create. Можно править параметры, а можно пересоздать операцию с новыми параметрами. Предусмотреть удаление (метод DELETE) незапущенной операции?"> --->
<cffunction name="get" hint="Список операций">
<cfargument name="pageSize" type="string" hint="type:integer" default="100"/>
<cfargument name="page" type="string" hint="type:integer" default="1"/>
<cfargument name="orderBy" type="string" hint="type:string, description:comma-separated list of fields to sort by, example:fld1.ASC,fld2.DESC,fld3.ASC" default=""/>
<cftry> <!--- отладка на время проблемы 404 --->
<cfset local={}/>
<!---parse and validate request parameters--->
<cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<!--- <cfset var order=this.helper.parseOrderBy(this.fieldsSpec, arguments.orderBy)/> --->
<cfcatch type="invalidParamValue"><!--- <cfreturn representationOf("Проверка связи 3")> --->
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
<cfset var out={}/>
<cfset var maxrows=arguments.pageSize*arguments.page/>
<cfset var startrow=arguments.pageSize*(arguments.page-1)+1/>
<!--- <cfset request.locateIamService()/> --->
<cfquery name="local.qRead" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
<m:field title="operation">o.operation</m:field>
<m:field title="instance_operation_uid">o.instance_operation_uid::text as instance_operation_uid</m:field>
<m:field title="instance_uid">o.instance_uid::text as instance_uid</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 title="dt_updated">to_char(o.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_updated</m:field>
<m:field title="Отправка">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 title="Начало">to_char(o.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_start</m:field>
<m:field title="Завершение">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 title="Успешно">o.is_successful</m:field>
<m:field title="Экземпляр">e.display_name</m:field>
<m:field title="service_id">e.service_id</m:field>
<m:field title="Услуга">v.svc</m:field>
<m:field>i.specification_item_id</m:field>
<m:field>s.specification_id</m:field>
<m:field>d.contract_id</m:field>
<m:field>o.creator_id</m:field>
<m:field>o.updater_id</m:field>
<m:field>c.login as creator_login</m:field>
<m:field>c.email as creator_email</m:field>
<m:field>u.login as updater_login</m:field>
<m:field>u.email as updater_email</m:field>
</m:field_set>
from instance_operation o
join instance e on (o.instance_uid=e.instance_uid)
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 svc v on (e.service_id=v.svc_id)
left outer join usr c on (e.creator_id=c.usr_id)
left outer join usr u on (u.updater_id=u.usr_id)
where
s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
<m:filter_build filter=#filter#/>
order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/><!---no sort length limit--->
limit #maxrows#
</cfquery>
<!--- <cfdump var=#local.qRead#/><cfabort/> --->
<cfquery name="local.qTotal">
select count(*) as cnt
from instance_operation o
join instance e on (o.instance_uid=e.instance_uid)
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)
where
s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
</cfquery>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.total"=#local.qTotal.cnt#/>
<cfset "out.resultSetSize"=#local.qRead.recordCount#/>
<cfset "out.pageSize"=#arguments.pageSize*1#/>
<cfset "out.page"=#arguments.page*1#/>
<cfset "out.orderBy"=#arguments.orderBy#/>
<cfset var resultCollection=[]/>
<cfloop query=#local.qRead# startRow=#startrow# endRow=#(startrow+maxrows-1)#>
<cfset var rec={}/>
<cfset this.helper.appendRecord(rec, "", local.titleMap, local.qRead, this.helper.snake2camel)/>
<cfset arrayAppend(resultCollection, rec)/>
</cfloop>
<cfset "out.size"=#arrayLen(resultCollection)#/>
<cfset "out.results"=#resultCollection#/>
<cfset "out.runDurationMs"=getTickCount()-request.startTickCount/>
<!---<cfset "out.sql"=#local.result.sql#/>--->
<cfreturn representationOf(out)/>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
</cffunction>
<cffunction name="post" hint="Создание новой операции. Отдельно загружаются CFS параметры, генерируются RFS параметры. Запускается операция отдельным вызовом. До запуска CFS параметры могут корректироваться (а надо?). Запущенная операция вместе с параметрами навсегда становится Read Only (при желании можем реализовать ее клонирование). Можем реализовать корректировку параметров, но непонятно, зачем. Когда мы конфигурируем инстанс, на самом деле мы задаем CFS параметры операции create. Можно править параметры, а можно пересоздать операцию с новыми параметрами. Предусмотреть удаление (метод DELETE) незапущенной операции?">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cfargument name="operation" type="string" required=true hint="type:string decsiption: operation name, e.g. create"/>
<!--- <cfargument name="resourceRealmId" type="string" default=-1 hint="type:integer decsiption: идентификатор ресурсной платформы"/> ---><!--- *** --->
<!--- *** полноценная валидация операции предполагает проверку, что операция поддерживается для данного сервиса и текущего состояния инстанса. Можно представить себе зависимость этого от параметров, но хотелось бы избежать такого усложнения --->
<!--- Можно отметить, что, например, допустимость операции delete зависит от контекста - сколько времени сервис остановлен --->
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfquery name="local.qCheckOperation" result="local.result">
select count(*) as cnt
from svc_operation so
join svc s on so.svc_id=s.svc_id
join instance e on (s.svc_id=e.service_id)
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
AND so.operation=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.operation#"/>
</cfquery>
<cfif local.qCheckOperation.cnt EQ 0>
<cfthrow type="invalidParamValue" message="Operation unsupported for this service/instance" detail="#arguments.operation#"/>
</cfif>
<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", "Instance is not accessible to the current user (at least does not belong to the default specification)")).withStatus(403)/>
</cfif>
<!--- *** Добавить проверку разрешенных переходов, например, create нельзя сделать на развернутом экземпляре
*** Вероятно, допустимые переходы нужно либо специфицировать в документации, либо явно опубликовать (инстанс может публиковать список допустимых операций) --->
<!--- <cfif listFind("create,redeploy",arguments.operation)>
<cfset checkResourceRealmId(arguments.resourceRealmId, arguments.instanceUid)/>
</cfif> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfset local.instanceOperationUid=#createGUID()#/>
<cftry>
<cfquery name="local.qSave">
insert into instance_operation
(instance_uid, instance_operation_uid, operation, dt_created, creator_id, dt_updated, updater_id)
values
(
<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" />
,<cfqueryparam cfsqltype="cf_sql_other" value="#local.instanceOperationUid#" />
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.operation#"/>
,<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Now()#" />
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usrId#" />
,<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Now()#" />
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usrId#" />
);
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData()
.withHeaders({location: "./#local.instanceOperationUid#"})
.withStatus(201, "Created")
/>
</cffunction>
<!--- *** NOT USED --->
<cffunction name="checkResourceRealmId" returntype="void" hint="deprecated">
<!--- В данном случае мы проверяем соответствие платформы сервису, а не операции, поэтому вопрос, нужен ли для данной операции параметр resourceRealm, надо решать вне этой функции --->
<cfargument name="resourceRealmId" type="numeric" required=true/>
<cfargument name="instanceUid" type="guid" required=true/>
<cfset var local={}/>
<cfif NOT resourceRealmId GT 0>
<cfthrow type="InvalidParamValue" message="Resource realm undefined" detail="Не выбрана ресурсная платформа"/><!--- *** для многих операций она и не нужна --->
</cfif>
<cfquery name="local.qContract">
select c.contract_id
from instance e
join specification_item si on (e.specification_item_id=si.specification_item_id)
join specification s on (si.specification_id=s.specification_id)
join contract c on (s.contract_id=c.contract_id)
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid("guid",arguments.instanceUid)#/>
</cfquery>
<cfquery name="local.qCheckResourceRealmType">
select r.resource_realm_type_id
from resource_realm r
join resource_realm_type t on (r.resource_realm_type_id=t.resource_realm_type_id)
join svc s on (t.resource_realm_type_id=s.resource_realm_type_id)
join instance e on (s.svc_id=e.service_id)
where r.resource_realm_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.resourceRealmId# null=#!isValid("integer",arguments.resourceRealmId)#/>
AND e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value=#arguments.instanceUid# null=#!isValid("guid", arguments.instanceUid)#/>
</cfquery>
<cfif local.qCheckResourceRealmType.recordCount EQ 0>
<cfthrow type="InvalidParamValue" message="Resource realm does not match service" detail="Ресурсная платформа не соответствует сервису"/>
</cfif>
<cfquery name="local.qCheckResourceRealmAccess">
select r.resource_realm_id
from resource_realm r
join resource_realm_access a on (r.resource_realm_id=a.resource_realm_id)
where r.resource_realm_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.resourceRealmId# null=#!isValid("integer",arguments.resourceRealmId)#/>
AND (a.contract_id=<cfqueryparam cfsqltype="CF_SQL_INTEGER" value=#local.qContract.contract_id#/>
OR a.contract_id=0)
AND a.is_enabled
</cfquery>
<cfif local.qCheckResourceRealmAccess.recordCount EQ 0>
<cfthrow type="InvalidParamValue" message="Access to the resource realm denied for current contract" detail="У текущего договора id=#local.qContract.contract_id# нет доступа к выбранной ресурсной платформе"/>
</cfif>
<!---
Проверить доступ (это предварительная проверка! настоящая проверка перед запуском операции)
Итого: фильтрация выпадающего списка
Проверка атрибута операции (из которого получается RFS параметр)
Проверка RFS параметров перед запуском операции
(Можно еще вставить параноидальную проверку при сабмите, когда мы формируем параметры запроса)
(Можно еще вставить параноидальную проверку в cmdb-api, чтобы застраховаться от дефектов админки)
--->
<cfreturn/>
</cffunction>
</cfcomponent>