532 lines
33 KiB
Plaintext
532 lines
33 KiB
Plaintext
<cfcomponent extends="taffy.core.resource" taffy:uri="/instances">
|
|
|
|
<cfsilent>
|
|
<cfimport prefix="m" taglib="../lib"/>
|
|
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
|
|
</cfsilent>
|
|
|
|
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
|
|
<cfset this.fieldsSpec={
|
|
"instance_uid"={prefix="inr", type="guid"},
|
|
"instance_config_dt_created"={prefix="inr", type="date"},
|
|
"instance_config_dt_updated"={prefix="inr", type="date"},
|
|
"display_name"={prefix="inr", type="string"},
|
|
"service_id"={prefix="inr", type="integer"},
|
|
"specification_item_id"={prefix="inr", type="integer"},
|
|
"specification"={prefix="inr", type="string"},
|
|
"specification_id"={prefix="inr", type="integer"},
|
|
"contract_id"={prefix="inr", type="integer"},
|
|
"svc"={prefix="inr", type="string"},
|
|
"code"={prefix="inr", type="string"},
|
|
"updater_id"={prefix="inr", type="integer"},
|
|
"updater_shortname"={prefix="inr", type="string"},
|
|
"updater_login"={prefix="inr", type="string"},
|
|
"last_operation"={prefix="inr", type="string"},
|
|
"last_operation_uid"={prefix="inr", type="guid"},
|
|
"submit_result"={prefix="inr", type="string"},
|
|
"operation_dt_start"={prefix="inr", type="date"},
|
|
"operation_dt_finish"={prefix="inr", type="date"},
|
|
"duration"={prefix="inr", type="numeric"},
|
|
"uptime"={prefix="inr", type="numeric"},
|
|
"dt_deploy"={prefix="inr", type="date"},
|
|
<!--- "instance_data"={prefix="inr", type="string"}, --->
|
|
"monitoring_url"={prefix="inr", type="string"},
|
|
"instance_state_uid"={prefix="inr", type="guid"},
|
|
"is_deleted"={prefix="inr", type="string"},<!--- not bool --->
|
|
"is_suspended"={prefix="inr", type="string"},<!--- not bool --->
|
|
"job_is_pending"={prefix="inr", type="string"},<!--- not bool --->
|
|
"resource_realm"={prefix="inr", type="string"},
|
|
"resource_realm_type_id"={prefix="inr", type="integer"},
|
|
"operation_is_in_progress"={prefix="inr", type="boolean"},
|
|
"operation_is_pending"={prefix="inr", type="boolean"},
|
|
"is_test"={prefix="inr", type="boolean"},
|
|
"is_auxiliary"={prefix="inr", type="boolean"},
|
|
"explained_status"={prefix="inr", type="string"},
|
|
"dt_state"={prefix="inr", type="date"},
|
|
"version"={prefix="inr", type="integer"}<!--- ,
|
|
"io_instance_uid"={prefix="inr", type="integer"},
|
|
"st_instance_uid"={prefix="inr", type="integer"} --->
|
|
}
|
|
/>
|
|
|
|
|
|
<cffunction name="get" hint="Список экземпляров сервисов, принадлежащих данному клиенту">
|
|
<cfargument name="pageSize" type="string" hint="type:integer" required=false default="100"/>
|
|
<cfargument name="page" type="string" hint="type:integer" required=false 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 (performance not assured on arbitrary sorting)" required=false default=""/>
|
|
<cfargument name="fields" type="string" hint="type:string, description:comma-separated list of fields to limit output to, empty string or omitted for default field set" required=false default=""/>
|
|
<cfargument name="search" type="string" hint="type:string, free search (black box)" required=false default=""/>
|
|
<!---сюда можно воткнуть спецификацию полей для сортировки и фильтрации, но только ручками, идея компактного кода еще более отдаляется--->
|
|
|
|
<!---parse and validate request parameters--->
|
|
<cfset local={}/>
|
|
<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)/>
|
|
|
|
<cfset local.fieldsToOutput=""/>
|
|
<!--- <cfif listLen(arguments.fields)> ---><!--- *** если идти от arguments.fields, будет лаконичнее --->
|
|
<cfloop collection=#this.fieldsSpec# item="fld">
|
|
<!--- <cfoutput>#this.helper.snake2camel(trim(fld))#</cfoutput> --->
|
|
<!--- *** в данном случае мы некорректно ограничиваем состав выводимых полей
|
|
полями, перечисленными в fieldSpec --->
|
|
<cfif listFind(arguments.fields, this.helper.snake2camel(trim(fld)))
|
|
OR len(arguments.fields) EQ 0
|
|
/* OR structKeyExists(order,this.helper.snake2camel(trim(fld))) */>
|
|
<cfset local.fieldsToOutput=listAppend(local.fieldsToOutput,trim(fld))/>
|
|
</cfif>
|
|
</cfloop>
|
|
<!--- </cfif> --->
|
|
|
|
<cfset local.fieldsToQueryFor = local.fieldsToOutput/>
|
|
<cfset local.fieldsShortList=""/>
|
|
|
|
<!--- теперь добавим в перечень полей, которые нужно оставить, поля для фильтрации и сортировки --->
|
|
<cfloop array=#filter# index="fltr">
|
|
<!--- <cfset var fltr = structFind(filter,item)/> --->
|
|
<cfif listLen(fltr.field,".") EQ 2>
|
|
<cfset var fld = listGetAt(fltr.field,2,".")/>
|
|
<cfelseif listLen(fltr.field,".") EQ 1>
|
|
<cfset var fld = fltr.field/>
|
|
<cfelse>
|
|
<cfreturn representationOf(this.helper.formatBadRequestError("Ошибка разбора фильтра")).withStatus(500)/>
|
|
</cfif>
|
|
<cfif structKeyExists(this.fieldsSpec,fld) AND NOT listFind(local.fieldsToQueryFor,fld)>
|
|
<cfset local.fieldsToQueryFor=listAppend(local.fieldsToQueryFor,fld)/>
|
|
</cfif>
|
|
<cfif structKeyExists(this.fieldsSpec,fld) AND NOT listFind(local.fieldsShortList,fld)>
|
|
<cfset local.fieldsShortList=listAppend(local.fieldsShortList,fld)/>
|
|
</cfif>
|
|
</cfloop>
|
|
<!--- *** Не нужно ли сохранять префикс? (на случай одноименных полей) --->
|
|
<cfloop collection=#order# item="item">
|
|
<cfset var ord = structFind(order,item)/>
|
|
<cfif listLen(ord.fld,".") EQ 2>
|
|
<cfset var fld = listGetAt(ord.fld,2,".")/>
|
|
<cfelseif listLen(ord.fld,".") EQ 1>
|
|
<cfset var fld = ord.fld/>
|
|
<cfelse>
|
|
<cfreturn representationOf(this.helper.formatBadRequestError("Ошибка разбора порядка сортировки")).withStatus(500)/>
|
|
</cfif>
|
|
<cfif structKeyExists(this.fieldsSpec,fld) AND NOT listFind(local.fieldsToQueryFor,fld)>
|
|
<!--- в данном случае мы не пытаемся поддерживать позиционную сортировку --->
|
|
<cfset local.fieldsToQueryFor=listAppend(local.fieldsToQueryFor,fld)/>
|
|
</cfif>
|
|
<cfif structKeyExists(this.fieldsSpec,fld) AND NOT listFind(local.fieldsShortList,fld)>
|
|
<cfset local.fieldsShortList=listAppend(local.fieldsShortList,fld)/>
|
|
</cfif>
|
|
</cfloop>
|
|
|
|
<cfcatch type="invalidParamValue">
|
|
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
|
|
</cfcatch>
|
|
</cftry>
|
|
|
|
<cftry><!--- *** времянка, пока глотается ошибка 500 --->
|
|
<!--- Такая бодяга. Кастомные сериализаторы не умеют вложенных структур (поразительно, или я не нашел как).
|
|
Стандартный сериализатор не умеет выводить дату в ISO 8601.
|
|
Приходится колхозить и форматировать на стороне БД (насилу подобрал формат).
|
|
Также на стороне БД приходится конвертировать GUID, потому что драйвер pg jdbc его представляет как структуру из 2 чисел.
|
|
Еще jsonb как-то странно сериализуется, в 3 поля, вместо одного Value (решается преобразованием ::text)
|
|
--->
|
|
|
|
<m:field_set titleMapOut="local.titleMap"
|
|
lengthOut="local.fieldCount"
|
|
nameListOut="local.definedRecordFields"
|
|
listOut="local.definedQueryFields"
|
|
fieldsToInclude=#local.fieldsToQueryFor#>
|
|
<m:field>to_char(e.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_created</m:field>
|
|
<m:field>to_char(e.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_updated</m:field>
|
|
<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.specification_item_id</m:field>
|
|
<m:field formatter=#request.castToBool#>e.is_auxiliary</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.resource_realm_type_id</m:field>
|
|
<!--- <m:field>p.param_value as resource_realm</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>io.operation as last_operation</m:field>
|
|
<m:field>io.instance_operation_uid::text as last_operation_uid</m:field>
|
|
<m:field>io.submit_result as submit_result</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>to_char(io.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as operation_dt_start</m:field>
|
|
<m:field>to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as operation_dt_finish</m:field>
|
|
<m:field>round(extract('epoch' from coalesce(io.dt_finish,CURRENT_TIMESTAMP)- io.dt_start),1) as duration</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>to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_deploy</m:field>
|
|
<!--- <m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data::text as instance_data</m:field> ---><!--- *** операция выглядит очень накладно, но можно это поле в дельнейшем не выводить --->
|
|
<m:field>st.instance_state_uid::text as instance_state_uid</m:field>
|
|
<m:field formatter=#request.castToBool#>st.is_test as is_test</m:field>
|
|
<m:field formatter=#request.castToBool#>coalesce(st.instance_data->>'isDeleted','false') as is_deleted</m:field><!--- attention it is not bool in state --->
|
|
<m:field formatter=#request.castToBool#>st.instance_data->>'isSuspended' as is_suspended</m:field>
|
|
<m:field formatter=#request.castToBool#>st.instance_data->'job'->>'pending' as job_is_pending</m:field>
|
|
<m:field>st.instance_data->'out'->'monitoring'->>'allDashboards' as monitoring_url</m:field>
|
|
<m:field>st.instance_data->'params'->>'resourceRealm' as resource_realm</m:field>
|
|
<m:field>to_char(st.dt_state, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_state</m:field>
|
|
<!--- <m:field>io.instance_uid::text as io_instance_uid</m:field>
|
|
<m:field>st.instance_uid::text as st_instance_uid</m:field> --->
|
|
<m:field>(case
|
|
when st.instance_uid IS NULL then 'not created'
|
|
when job_is_pending = 'true' then 'pending'
|
|
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 io.instance_uid IS NULL then 'not configured'
|
|
else null end
|
|
) as explained_status</m:field>
|
|
<m:field>st.version</m:field>
|
|
</m:field_set>
|
|
|
|
|
|
<cfquery name="local.qCnt" result="local.result">
|
|
select count(*) as cnt
|
|
from (
|
|
select
|
|
<!--- из-за фильтраци по вложенному селекту нам нужны значения колонок --->
|
|
<cfif arrayIsEmpty(filter)>
|
|
'' as placeholder
|
|
<cfelse>
|
|
#preserveSingleQuotes(definedQueryFields)#
|
|
</cfif>
|
|
from instance e
|
|
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)
|
|
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->'job'->>'pending') as job_is_pending
|
|
,(ist.instance_data->'out'->'monitoring'->>'allDashboards') as monitoring_url
|
|
,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)
|
|
<!--- left outer join instance_cfs_param p on (e.instance_uid=p.instance_uid AND p.param='resourceRealm') --->
|
|
where s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
|
|
<cfif len(arguments.search)>
|
|
AND (
|
|
(e.display_name) ilike (<cfqueryparam cfsqltype="cf_sql_varchar" value='%#arguments.search#%'/>)
|
|
OR ((coalesce(v.svc,'') ||' '|| coalesce(v.svc_short,'') ||' '|| coalesce(v.synonyms,''))) ilike (<cfqueryparam cfsqltype="cf_sql_varchar" value='%#arguments.search#%'/>)
|
|
)
|
|
</cfif>
|
|
) inr
|
|
where 1=1 <m:filter_build filter=#filter#/>
|
|
</cfquery>
|
|
|
|
<!--- <cfdump var=#filter#/><cfabort/> --->
|
|
|
|
|
|
<cfquery name="local.qRead" result="local.result">
|
|
select
|
|
<cfif listLen(local.fieldsToOutput)>#local.fieldsToOutput#<cfelse>*</cfif>
|
|
from (
|
|
select
|
|
#preserveSingleQuotes(definedQueryFields)#
|
|
from instance e
|
|
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)
|
|
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->'job'->>'pending') as job_is_pending
|
|
,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)
|
|
<!--- left outer join instance_cfs_param p on (e.instance_uid=p.instance_uid AND p.param='resourceRealm') --->
|
|
where s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/><!--- *** make project entity --->
|
|
<!--- u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.usrId#/> --->
|
|
<cfif len(arguments.search)>
|
|
AND (
|
|
(e.display_name) ilike (<cfqueryparam cfsqltype="cf_sql_varchar" value='%#arguments.search#%'/>)
|
|
OR ((coalesce(v.svc,'') ||' '|| coalesce(v.svc_short,'') ||' '|| coalesce(v.synonyms,''))) ilike (<cfqueryparam cfsqltype="cf_sql_varchar" value='%#arguments.search#%'/>)
|
|
)
|
|
</cfif>
|
|
|
|
) inr
|
|
where 1=1 <m:filter_build filter=#filter#/>
|
|
<!--- order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/> ---><!--- из-за нефиксированного числа колонок труднее становится использовать цифровую нотацию --->
|
|
order by <m:order_build sortCollection=#order# fieldCount=0 defaultOrder="1 desc"/><!---no sort length limit--->
|
|
limit #arguments.pageSize#
|
|
offset #arguments.pageSize*(arguments.page-1)#
|
|
</cfquery>
|
|
<!---<cfdump var=#arguments#/><cfdump var=#local.qRead#/><cfabort/> --->
|
|
|
|
<cfquery name="local.qTotal">
|
|
select count(*) as cnt
|
|
from instance e
|
|
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)
|
|
|
|
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 (st.instance_uid=e.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)
|
|
|
|
<!--- specificationId инжектируется в arguments scope в Application.cfc --->
|
|
where s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
|
|
<!--- u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.usrId#/> --->
|
|
</cfquery>
|
|
|
|
<cfset var out=structNew("linked")/>
|
|
|
|
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
|
|
<cfset "out.total"=#local.qTotal.cnt#/>
|
|
<cfset "out.resultSetSize"=#local.qCnt.cnt#/>
|
|
<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=structNew("linked")/>
|
|
<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"> <!--- {"message"=cfcatch.message, "detail"=cfcatch.detail} --->
|
|
<cfreturn representationOf(cfcatch).withStatus(500)/>
|
|
</cfcatch>
|
|
</cftry>
|
|
</cffunction><!--- get --->
|
|
|
|
|
|
<cffunction name="post" hint="ВНИМАНИЕ! У текущего контрагента должна быть спецификация, в которую будет вписан создаваемый экземпляр. Новая пустая запись экземпляра. Фактическое развертывание выполняется отдельно,асинхронной операцией create сконфигурированного экземпляра. Одновременно мы создаем строку в спецификации по умолчанию и связываем экземпляр с этой строкой. Первичный ключ генерируется. Текущий пользователь, контракт, спецификация получаются из контекста.">
|
|
<cfargument name="displayName" type="string" required=true hint="type:string"/>
|
|
<cfargument name="serviceId" type="string" required=true hint="type:integer"/>
|
|
<cfargument name="descr" type="string" required=false default="" hint="type:varchar (no cleanup here!)"/>
|
|
<cfargument name="resourceRealmId" type="string" required=false default="-1" hint="type:integer"/>
|
|
<!--- <cfargument name="instanceUid" type="string" required=false default="" hint="type:guid)"/>
|
|
<cfargument name="usrUid" type="string" required=true hint="type:guid"/>
|
|
<cfargument name="companyUid" type="string" required=true hint="type:guid"/> --->
|
|
<!--- оказывается, их можно и не декларировать --->
|
|
|
|
<cfset var local={}/>
|
|
|
|
<cfset local.instanceUid=#createGUID()#/>
|
|
|
|
<cftry>
|
|
<cfset this.helper.keyExistsAndValid(arguments, "serviceId", "integer")/>
|
|
<cfset checkDisplayName(arguments.displayName, arguments.usrId, local.instanceUid)/>
|
|
|
|
<!--- делаем вставку в спецификацию и таблицу экземпляров --->
|
|
<cfquery name="local.qSpecItem">
|
|
insert into specification_item
|
|
(specification_id, specification_item, svc_id, quantity, price, dt_created, creator_id, dt_updated, updater_id)
|
|
values
|
|
(
|
|
<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.specificationId#" />
|
|
,<cfqueryparam cfsqltype="cf_sql_varchar" value=""/>
|
|
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.serviceId#" />
|
|
,<cfqueryparam cfsqltype="cf_sql_integer" value="1" /> <!--- это типично для всех PAYG --->
|
|
,<cfqueryparam cfsqltype="cf_sql_numeric" null=true />
|
|
,<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#" />
|
|
) returning specification_item_id;
|
|
</cfquery>
|
|
|
|
<cfquery name="local.qSave">
|
|
insert into instance
|
|
(instance_uid, display_name, service_id, specification_item_id, descr, dt_created, creator_id, dt_updated, updater_id)
|
|
values
|
|
(
|
|
<cfqueryparam cfsqltype="cf_sql_other" value="#local.instanceUid#" />
|
|
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#htmlEditFormat(arguments.displayName)#"/>
|
|
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.serviceId#" />
|
|
,<cfqueryparam cfsqltype="cf_sql_integer" value="#local.qSpecItem.specification_item_id#" />
|
|
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#htmlEditFormat(arguments.descr)#" />
|
|
,<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="invalidParamValue">
|
|
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
|
|
</cfcatch>
|
|
</cftry>
|
|
|
|
<cfreturn noData()
|
|
.withHeaders({location: "./#local.instanceUid#"})
|
|
.withStatus(201, "Created")
|
|
/>
|
|
</cffunction><!--- post --->
|
|
|
|
|
|
|
|
<cffunction name="checkDisplayName">
|
|
<cfargument name="displayName" required=true/>
|
|
<cfargument name="usrId" type="numeric" required=true/>
|
|
<cfargument name="instanceUid" type="guid" required=true/>
|
|
|
|
<cfset var local={}/>
|
|
<cfset var display_name=trim(lcase(arguments.displayName))/>
|
|
|
|
<cfif len(display_name) LT 3>
|
|
<cfthrow type="InvalidParamValue" message="Instance Display Name too short" detail="Имя должно быть не короче 3 символов"/>
|
|
<cfelseif len(display_name) GT 80>
|
|
<cfthrow type="InvalidParamValue" message="Instance Display Name too long" detail="Имя должно быть не длиннее 80 символов"/>
|
|
<cfelse>
|
|
<cfquery name="local.qUniqueDisplayName">
|
|
select count(*) as cnt from instance e
|
|
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 usr u on (k.contragent_id=u.contragent_id)
|
|
where u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.usrId#/>
|
|
AND lower(e.display_name)=lower(<cfqueryparam cfsqltype="cf_sql_varchar" value=#arguments.displayName#/>)
|
|
AND e.instance_uid <> <cfqueryparam cfsqltype="cf_sql_other" value=#arguments.instanceUid#/>
|
|
AND (select cast(st.instance_data->>'isDeleted' as boolean) from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) IS NOT TRUE
|
|
/*postgre specific. should take into account instances without state, ignoring explicitly deleted only*/
|
|
</cfquery>
|
|
<cfif local.qUniqueDisplayName.cnt GT 0>
|
|
<cfthrow type="InvalidParamValue" message="Instance Display Name not unique" detail="Имя экземпляра должно быть уникальным в рамках договора"/>
|
|
</cfif>
|
|
</cfif>
|
|
</cffunction>
|
|
|
|
<cffunction name="generateDefaultName" returnType="string">
|
|
<!--- *** Это некорректно, потому что имя может содержать пробелы и т.п. --->
|
|
<!--- *** Неуклюже и медленно --->
|
|
<!--- Эти валидации и генерации занимают удивительно много рабочего времени --->
|
|
<cfargument name="service_id" type="numeric"/>
|
|
<cfargument name="usr_id" type="numeric"/>
|
|
|
|
<cfset var local={}/>
|
|
|
|
<cfquery name="local.qSvc">
|
|
select coalesce(svc_short,svc) as svc_short from svc where svc_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.service_id#/>
|
|
</cfquery>
|
|
|
|
<cfquery name="local.qInst">
|
|
select display_name
|
|
from instance e
|
|
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 usr u on (k.contragent_id=u.contragent_id)
|
|
where u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.usr_id#/>
|
|
AND lower(e.display_name) like lower(<cfqueryparam cfsqltype="cf_sql_varchar" value="#local.qSvc.svc_short#%"/>)
|
|
AND service_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.service_id#/>
|
|
</cfquery>
|
|
|
|
<cfset var prefixLen=len(local.qSvc.svc_short)+1/>
|
|
<cfset var i=1/>
|
|
<cfloop query="local.qInst">
|
|
<cfif len(local.qInst.display_name) GT prefixLen>
|
|
<cfset var suffix=right(local.qInst.display_name, len(local.qInst.display_name)-prefixLen)/>
|
|
<cfif isValid("integer",suffix)>
|
|
<cfset i=(i GT int(suffix))?i:int(suffix)/>
|
|
</cfif>
|
|
</cfif>
|
|
</cfloop>
|
|
|
|
<cfreturn "#local.qSvc.svc_short#-#i+1#"/>
|
|
</cffunction>
|
|
|
|
</cfcomponent>
|
|
|
|
<!---
|
|
select
|
|
updater_shortname,duration,specification_id,display_name,contract_id,instance_config_dt_created,operation_is_pending,code,updater_login,operation_is_in_progress,instance_config_dt_updated,is_auxiliary,svc,service_id,dt_state,last_operation,submit_result,instance_state_uid,dt_deploy,is_test,specification_item_id,is_deleted,version,resource_realm,updater_id,operation_dt_start,operation_dt_finish,explained_status,last_operation_uid,uptime,instance_uid,suspended,instance_data,specification
|
|
from (
|
|
select
|
|
to_char(e.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_created,to_char(e.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as instance_config_dt_updated,e.instance_uid::text as instance_uid,e.display_name,e.service_id,e.specification_item_id,e.is_auxiliary,d.contract_id,i.specification_id,s.specification,v.svc,v.code,e.updater_id,u.login as updater_login,u.shortname as updater_shortname,(select io.operation from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as last_operation,(select io.instance_operation_uid::text from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as last_operation_uid,(select io.submit_result from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as submit_result,(select coalesce((io.dt_start IS NOT NULL AND io.dt_finish IS NULL AND io.submit_result='201'),false) from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_is_in_progress,(select coalesce((io.dt_start IS NULL AND io.submit_result='201'),false) from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_is_pending,(select to_char(io.dt_start, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_start,(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as operation_dt_finish,(select round(extract('epoch' from coalesce(io.dt_finish,CURRENT_TIMESTAMP)- io.dt_start),1) from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as duration,(select case
|
|
when io.operation IN ('delete', 'suspend') AND io.is_successful then 0
|
|
when io.is_successful then extract('epoch' from CURRENT_TIMESTAMP - coalesce(io.dt_finish,CURRENT_TIMESTAMP))
|
|
else 0 end
|
|
from instance_operation io
|
|
where io.instance_uid=e.instance_uid
|
|
order by dt_submit desc
|
|
limit 1)
|
|
as uptime,(select to_char(io.dt_finish, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_operation io where io.instance_uid=e.instance_uid AND io.is_successful AND io.operation='create' order by dt_submit desc limit 1) as dt_deploy,(select instance_data::text from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as instance_data,(select st.instance_state_uid::text from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as instance_state_uid,(select st.is_test from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as is_test,coalesce((select st.instance_data->>'isDeleted' from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1),'false') as is_deleted,(select st.instance_data->>'isSuspended' from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as suspended,(select cast(st.instance_data->>'params' as json)->>'resourceRealm' from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as resource_realm,(select to_char(st.dt_state, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) as dt_state,
|
|
(case
|
|
when
|
|
(select st.instance_data->>'isDeleted' from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) = 'true' then 'deleted'
|
|
when
|
|
(select st.instance_data->>'isSuspended' from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) = 'true' then 'suspended'
|
|
when
|
|
(select coalesce(st.instance_data->>'isDeleted','false') from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) = 'false'
|
|
AND
|
|
(select coalesce(st.instance_data->>'isSuspended','false') from instance_state st where st.instance_uid=e.instance_uid order by version desc limit 1) = 'false'
|
|
then 'running'
|
|
when
|
|
not exists (select * from instance_operation o where o.instance_uid=e.instance_uid) then 'not configured'
|
|
when
|
|
not exists (select * from instance_state st where st.instance_uid=e.instance_uid) then 'not created'
|
|
else null end
|
|
) as explained_status
|
|
,(select max(version) from instance_state st where st.instance_uid=e.instance_uid) as version
|
|
from instance e
|
|
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)
|
|
left outer join usr u on (e.updater_id=u.usr_id)
|
|
where s.specification_id=2
|
|
) inr
|
|
where 1=1
|
|
order by 1 desc
|
|
limit 100
|
|
offset 100
|
|
---> |