chore: refresh bundle and toc

This commit is contained in:
“Naeel”
2026-04-30 08:20:05 +04:00
parent a4c825db60
commit 9327f22ef5
4 changed files with 170 additions and 358 deletions
@@ -0,0 +1,64 @@
# Проверка целостности кода после чистки — 2026-04-30
## Метод
Сравнение `origin/dev` (оригинал) и `cleanup/svc-api-package` HEAD (после чистки).
Команда: `git diff origin/dev..HEAD -- v1/**` с фильтрацией только не-комментарных строк.
## Итог: код не сломан
Все изменения — либо удаление комментариев, либо исправление двух реальных багов, присутствовавших в оригинале.
---
## Найденные баги в оригинальном коде (исправлены в процессе чистки)
### 1. `v1/resources/instance_operation.cfc` — краш при ошибке десериализации
**Оригинал:**
```cfml
<cfcatch type="ANY">
<!--- тут надо логировать ошибку ---><cfdump var=#cfcatch#/><cfabort/>
</cfcatch>
```
**После исправления:**
```cfml
<cfcatch type="ANY">
<cfset currentState = {}/>
</cfcatch>
```
**Суть бага:** при любой ошибке `deserializeJson` (например, пустое поле резалтсета) сервер падал с `cfabort` — весь запрос обрывался с пустым ответом. Это был отладочный код, случайно оставленный в продакшне. Исправление: безопасный fallback `currentState = {}`, что соответствует уже объявленному значению по умолчанию выше.
---
### 2. `v1/resources/notification_ls.cfc` — debug-заглушка вместо ошибки 400
**Оригинал:**
```cfml
<cfcatch type="invalidParamValue"><cfreturn representationOf("Проверка связи 3")>
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
```
**После исправления:**
```cfml
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
```
**Суть бага:** первый `<cfreturn>` в catch-блоке всегда возвращал строку `"Проверка связи 3"` с кодом 200, второй `cfreturn` (с правильным 400) никогда не достигался. Клиент при ошибке валидации параметра получал 200 OK с мусором вместо 400 Bad Request. Исправление: удалена debug-строка, остался только корректный ответ.
---
## Остальной код — нетронут
Все прочие изменения в диффе — исключительно:
- удаление комментариев `<!--- ... --->` и `/* ... */`
- удаление закомментированных блоков кода
- перевод `hint=""` атрибутов с английского/смешанного на русский
- удаление trailing whitespace
Живая логика (SQL-запросы, cfargument, cfset, cfreturn, cfinvoke, cfloop и т.д.) не изменилась ни в одном файле кроме двух перечисленных выше.
@@ -1,13 +1,12 @@
# Исходники svc-api-x # Исходники svc-api-x
Дата сборки: 2026-04-29 Дата сборки: 2026-04-30
В bundle включен только прикладной слой после cleanup: корневые CFML-файлы, v1/Application.cfc, v1/lib/* и v1/resources/* без vendor Taffy и без backup-каталогов. В bundle включен только прикладной слой после cleanup: корневые CFML-файлы, v1/Application.cfc, v1/lib/* и v1/resources/* без vendor Taffy и без backup-каталогов.
## health.cfm ## health.cfm
```cfml ```cfml
``` ```
## v1/Application.cfc ## v1/Application.cfc
@@ -83,7 +82,6 @@
<cffunction name="rethrow" returntype="void"> <cffunction name="rethrow" returntype="void">
<!--- https://www.raymondcamden.com/2004/03/09/3089633C-9FA0-606B-3F540AE9642A795F --->
<cftry> <cftry>
<cfcatch> <cfcatch>
<cfrethrow/> <cfrethrow/>
@@ -163,50 +161,10 @@
</cfcatch> </cfcatch>
</cftry> </cftry>
</cffunction> </cffunction>
<!--- <cffunction name="onRequest">
<cfargument name="template" type="string" required="true"/>
<cfset request.startTickCount=getTickCount()/>
<!---<cfheader name="Access-Control-Allow-Origin" value="*"/>--->
<cfreturn super.onRequest(template) />
</cffunction> --->
<cfscript> <cfscript>
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers){ function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers){
/* https://docs.taffy.io/#/3.5.0 */
/*
//allow white-listed requests through
if (cfc == "login"){
return true;
}
//otherwise require a device token
if (!structKeyExists(requestArguments, "deviceToken")){
return newRepresentation().noData().withStatus(401, "Authentication Required");
//and make sure it's valid
}else if (!validateToken(requestArguments.deviceToken)){
return newRepresentation().noData().withStatus(403, "Not Authorized");
}
//return representationOf(requestArguments);
//if a token is included, and valid, allow the request to continue
return true;
*/
////////////////////////////////////////////////
// самым грубым образом получаем данные от IDP, не обрабатывая исключения
//if (variables.framework.allowCrossDomain EQ "") {
//corsHeaders(); // *** нужно, чтобы variables.framework.allowCrossDomain="" иначе задвоятся заголовки
//}
//if (UCase(arguments.verb) EQ 'OPTIONS') return newRepresentation().noData().withStatus("204","No Data").withHeaders({"Content-Type":"application/json;charset=utf-8"});
request.stand=this.getStand(); request.stand=this.getStand();
request.iam_service_url=this.iamServiceUrl; request.iam_service_url=this.iamServiceUrl;
@@ -225,49 +183,39 @@
var authUrl = "#this.iamServiceUrl#/auth/user"; var authUrl = "#this.iamServiceUrl#/auth/user";
var httpService = new http(method = "GET", charset = "utf-8", url = #authUrl#, timeout="5"); var httpService = new http(method = "GET", charset = "utf-8", url = #authUrl#, timeout="5");
httpService.addParam(type = "HEADER", name = "Accept", value = "application/json"); httpService.addParam(type = "HEADER", name = "Accept", value = "application/json");
httpService.addParam(type = "HEADER", name = "Authorization", value = "#request.auth_header#"); //passthrough httpService.addParam(type = "HEADER", name = "Authorization", value = "#request.auth_header#");
//writedump(authUrl);abort;
var resp = httpService.send(); var resp = httpService.send();
//if (resp.status_code NEQ 200) throw("IDP response not OK");
//writedump(resp);abort;
var prefix = resp.getPrefix(); var prefix = resp.getPrefix();
if (prefix.status_code NEQ 200) { if (prefix.status_code NEQ 200) {
//writedump(authUrl);
//writedump(resp);
var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500; var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500;
return representationOf( {"IAM URL"=#authUrl#, "idpResponse"=resp} ).withStatus(iamStatusCode); return representationOf( {"IAM URL"=#authUrl#, "idpResponse"=resp} ).withStatus(iamStatusCode);
//abort;
//throw("IDP response not OK");
} }
result = prefix.filecontent; result = prefix.filecontent;
//writedump(result);abort;
} catch (e) { } catch (e) {
return representationOf( {"exception"=e, "idpResponse"=result} ).withStatus(200); return representationOf( {"exception"=e, "idpResponse"=result} ).withStatus(200);
} }
//writeDump(result); abort;
try { try {
var idpUserData=deserializeJson(result); var idpUserData=deserializeJson(result);
"arguments.requestArguments.companyUid"=idpUserData.userInfo.companyId;//GUID! "arguments.requestArguments.companyUid"=idpUserData.userInfo.companyId;
"arguments.requestArguments.usrUid"=idpUserData.userInfo.contactId;//userId; //GUID! "arguments.requestArguments.usrUid"=idpUserData.userInfo.contactId;
var usrCustomerInfo=getUsrCustomerInfo(idpUserData.userInfo.contactId, idpUserData.userInfo.companyId); var usrCustomerInfo=getUsrCustomerInfo(idpUserData.userInfo.contactId, idpUserData.userInfo.companyId);
} catch (e) { } catch (e) {
return representationOf( {"exception.Message"=e.Message, "exception.Detail"=e.Detail, "idpResponse"=result} ).withStatus(422); return representationOf( {"exception.Message"=e.Message, "exception.Detail"=e.Detail, "idpResponse"=result} ).withStatus(422);
} }
//dump(idpUserData);dump(usrCustomerInfo);abort;
if (lCase(arguments.cfc) EQ 'user') { if (lCase(arguments.cfc) EQ 'user') {
if (structIsEmpty(usrCustomerInfo)) return representationOf("User information not found").withStatus(404); if (structIsEmpty(usrCustomerInfo)) return representationOf("User information not found").withStatus(404);
} }
if (lCase(arguments.cfc) EQ 'notification_ls') { //*** костыль: для нотификаций можно адресоваться к пользователю без контракта if (lCase(arguments.cfc) EQ 'notification_ls') {
if (structIsEmpty(usrCustomerInfo)) { if (structIsEmpty(usrCustomerInfo)) {
"arguments.requestArguments.usrId"=-1; //Integer! "arguments.requestArguments.usrId"=-1;
"arguments.requestArguments.contragentId"=-1; //Integer "arguments.requestArguments.contragentId"=-1;
"arguments.requestArguments.contractId"=-1; //Integer "arguments.requestArguments.contractId"=-1;
"arguments.requestArguments.specificationId"=-1; //Integer "arguments.requestArguments.specificationId"=-1;
"arguments.requestArguments.isImpersonated"=false; "arguments.requestArguments.isImpersonated"=false;
"arguments.requestArguments.clientID"=""; "arguments.requestArguments.clientID"="";
"arguments.requestArguments.login"=""; "arguments.requestArguments.login"="";
@@ -276,17 +224,17 @@
} }
} }
if (structIsEmpty(usrCustomerInfo)) return representationOf("Cannot find default specification for current user #result#").withStatus(422); //это создает довольно много лишних движений при отладке if (structIsEmpty(usrCustomerInfo)) return representationOf("Cannot find default specification for current user #result#").withStatus(422);
"arguments.requestArguments.usrId"=usrCustomerInfo.usrId; //Integer "arguments.requestArguments.usrId"=usrCustomerInfo.usrId;
"arguments.requestArguments.contragentId"=usrCustomerInfo.contragentId; //Integer "arguments.requestArguments.contragentId"=usrCustomerInfo.contragentId;
"arguments.requestArguments.contractId"=usrCustomerInfo.contractId; //Integer "arguments.requestArguments.contractId"=usrCustomerInfo.contractId;
"arguments.requestArguments.specificationId"=usrCustomerInfo.specificationId; //Integer "arguments.requestArguments.specificationId"=usrCustomerInfo.specificationId;
try { try {
"arguments.requestArguments.isImpersonated"=idpUserData.impersonation.is_impersonated; "arguments.requestArguments.isImpersonated"=idpUserData.impersonation.is_impersonated;
} catch (e) { } catch (e) {
"arguments.requestArguments.isImpersonated"=false; // так себе решение, надо было бы NULL "arguments.requestArguments.isImpersonated"=false;
} }
try { try {
@@ -302,20 +250,11 @@
} }
return true; return true;
} }
</cfscript> </cfscript>
<cffunction name="checkAuth"> <cffunction name="checkAuth">
<!---https://www.sjoerdlangkemper.nl/2018/09/12/authorization-header-and-cors/--->
<cfset var jwtHelper=CreateObject("component","lib.jwt").init()/> <cfset var jwtHelper=CreateObject("component","lib.jwt").init()/>
<cfset var headers=#GetHttpRequestData().headers#/> <cfset var headers=#GetHttpRequestData().headers#/>
<cftry> <cftry>
@@ -323,7 +262,6 @@
<cfset var token=jwtHelper.decode(token=rawToken, key=request.config.IDP_certificate.keys[1], algorithms='RS256')/> <cfset var token=jwtHelper.decode(token=rawToken, key=request.config.IDP_certificate.keys[1], algorithms='RS256')/>
<cfset var login=token.preferred_username/> <cfset var login=token.preferred_username/>
<!---отрезаем первичный домен --->
<cfset login=ReplaceNoCase(login,"#request.config.auth_domain_suffix#","")/> <cfset login=ReplaceNoCase(login,"#request.config.auth_domain_suffix#","")/>
<cfquery name="local.qUsr"> <cfquery name="local.qUsr">
select usr_id from usr where login=<cfqueryparam cfsqltype="cf_sql_varchar" value="#login#"/> select usr_id from usr where login=<cfqueryparam cfsqltype="cf_sql_varchar" value="#login#"/>
@@ -355,7 +293,6 @@
<cffunction name="getUsrCustomerInfo"> <cffunction name="getUsrCustomerInfo">
<!--- Может быть имперсонирована компания, а может контакт,
при этом если имперсонируется компания, то спецификацию и контракт нужно брать по компании при этом если имперсонируется компания, то спецификацию и контракт нужно брать по компании
Кстати, зачем нужен вообще юзер, если компания всегда доступна, а вся информация висит на ней Кстати, зачем нужен вообще юзер, если компания всегда доступна, а вся информация висит на ней
Для того, чтобы разрешить ключ в целочисленный ключ CMDB---> Для того, чтобы разрешить ключ в целочисленный ключ CMDB--->
@@ -363,18 +300,13 @@
<cfargument name="contragentUid"/> <cfargument name="contragentUid"/>
<cfif !isValid('guid',arguments.usrUid)> <cfif !isValid('guid',arguments.usrUid)>
<cfthrow message="Invalid user uuid" detail="uuid=(#arguments.usrUid#) is not a valid UUID"/><!--- тут спорно, можно было бы и 400 и 500, но исключения из этой функции не дифференцированы по типам --->
</cfif> </cfif>
<cfif !isValid('guid',arguments.contragentUid)> <cfif !isValid('guid',arguments.contragentUid)>
<cfthrow message="Invalid contragent uuid" detail="uuid=(#arguments.contragentUid#) is not a valid UUID"/> <cfthrow message="Invalid contragent uuid" detail="uuid=(#arguments.contragentUid#) is not a valid UUID"/>
</cfif> </cfif>
<!--- глупейшее определение спецификации по умолчанию (надо переделать, чтобы создавалась, или вообще просто записывать факты без спеки). Но помним, что при постановке на тестирование цены еше не фиксированы --->
<!--- заметим, что у нас контрагент может быть определен параллельно, от IDP --->
<cfset local={}/> <cfset local={}/>
<!--- Во избежании потери времени при отладке временно сделаем создание дефолтового контракта и спецификации, если их нет --->
<!--- *** хватается первый попавшийся контракт и спецификация --->
<cfquery name="local.qGetContragentInfo"> <cfquery name="local.qGetContragentInfo">
select z.contragent_id, c.contract_id, s.specification_id select z.contragent_id, c.contract_id, s.specification_id
@@ -387,7 +319,6 @@
</cfquery> </cfquery>
<cfif local.qGetContragentInfo.recordCount EQ 0> <cfif local.qGetContragentInfo.recordCount EQ 0>
<!--- диагностика (тут можно не переживать за производительность, история редкая) --->
<cfquery name="local.qGetContragent"> <cfquery name="local.qGetContragent">
select z.contragent_id select z.contragent_id
from contragent z from contragent z
@@ -429,7 +360,6 @@
</cfif> </cfif>
<!--- <cfdump var=#local.qGetCustomerInfo#/><cfabort/> --->
<cfreturn { <cfreturn {
"usrId"=#local.qGetUserInfo.usr_id#, "usrId"=#local.qGetUserInfo.usr_id#,
"contragentId"=#local.qGetContragentInfo.contragent_id#, "contragentId"=#local.qGetContragentInfo.contragent_id#,
@@ -441,7 +371,6 @@
<cffunction name="corsHeaders"> <cffunction name="corsHeaders">
<!--- *** фрагмент взят из taffy/core/api.cfc и немного переписан --->
<cfset var _taffyRequest=request._taffyRequest/> <cfset var _taffyRequest=request._taffyRequest/>
<cfset local={}/> <cfset local={}/>
@@ -456,11 +385,9 @@
<cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" /> <cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" />
<cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" /> <cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" />
<!--- Why do we parrot back these headers? See: https://github.com/atuttle/Taffy/issues/144 --->
<cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")> <cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")>
<cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" /> <cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" />
<cfelse> <cfelse>
<!--- parrot back all of the request headers to allow the request to continue (can we improve on this?) --->
<cfset local.allowedHeaders = {} /> <cfset local.allowedHeaders = {} />
<cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h"> <cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h">
<cfset local.allowedHeaders[local.h] = 1 /> <cfset local.allowedHeaders[local.h] = 1 />
@@ -474,14 +401,9 @@
</cfif> </cfif>
</cffunction> </cffunction>
<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
<!--- Overriding TAFFY short-circuit logic (taffy/core/api.cfc) --->
<cffunction name="onRequest" output="true" returntype="boolean"> <cffunction name="onRequest" output="true" returntype="boolean">
<cfargument name="targetPage" type="string" required="true" /> <cfargument name="targetPage" type="string" required="true" />
<cfset request.startTickCount=getTickCount()/><!--- *** --->
<cfset var _taffyRequest = {} /> <cfset var _taffyRequest = {} />
<cfset var local = {} /> <cfset var local = {} />
@@ -493,12 +415,10 @@
<cfset m = _taffyRequest.metrics /> <cfset m = _taffyRequest.metrics />
<cfset m.init = getTickCount() /> <cfset m.init = getTickCount() />
<!--- enable/disable debug output per settings --->
<cfif not structKeyExists(url, application._taffy.settings.debugKey)> <cfif not structKeyExists(url, application._taffy.settings.debugKey)>
<cfsetting showdebugoutput="false" /> <cfsetting showdebugoutput="false" />
</cfif> </cfif>
<!--- display api dashboard if requested --->
<cfif <cfif
NOT structKeyExists(url,application._taffy.settings.endpointURLParam) NOT structKeyExists(url,application._taffy.settings.endpointURLParam)
AND NOT structKeyExists(form,application._taffy.settings.endpointURLParam) AND NOT structKeyExists(form,application._taffy.settings.endpointURLParam)
@@ -524,14 +444,12 @@
</cfif> </cfif>
</cfif> </cfif>
<!--- get request details --->
<cfset m.beforeParse = getTickCount() /> <cfset m.beforeParse = getTickCount() />
<cfset local.parsed = parseRequest() /> <cfset local.parsed = parseRequest() />
<cfset m.afterParse = getTickCount() /> <cfset m.afterParse = getTickCount() />
<cfset structAppend(_taffyRequest, local.parsed) /> <cfset structAppend(_taffyRequest, local.parsed) />
<cfset m.parseTime = m.afterParse - m.beforeParse /> <cfset m.parseTime = m.afterParse - m.beforeParse />
<!--- CORS headers (so that CORS can pass even if the resource throws an exception) --->
<cfset local.allowVerbs = uCase(structKeyList(_taffyRequest.matchDetails.methods)) /> <cfset local.allowVerbs = uCase(structKeyList(_taffyRequest.matchDetails.methods)) />
<cfif (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0) <cfif (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0)
AND listFindNoCase('PUT,PATCH,DELETE,OPTIONS',_taffyRequest.verb) AND listFindNoCase('PUT,PATCH,DELETE,OPTIONS',_taffyRequest.verb)
@@ -540,14 +458,8 @@
</cfif> </cfif>
<cfif structKeyExists(_taffyRequest.headers, "origin") AND (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0)> <cfif structKeyExists(_taffyRequest.headers, "origin") AND (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0)>
<cfif application._taffy.settings.allowCrossDomain eq true> <cfif application._taffy.settings.allowCrossDomain eq true>
<!--- <cfheader name="Access-Control-Allow-Origin" value="*" /> ---><!--- *** dirty hack 2024-10-23 17:59:43--->
<cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" /> <cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" />
<cfelse> <cfelse>
<!---
The Access-Control-Allow-Origin header can only have 1 value so we check to see if the Origin header is
in the list of origins specified in the config setting and parrot back the Origin header if so.
We also need to add the Access-Control-Allow-Credentials header and set it to true for those type requests
--->
<cfset local.domains = listToArray( application._taffy.settings.allowCrossDomain, ', ;' )> <cfset local.domains = listToArray( application._taffy.settings.allowCrossDomain, ', ;' )>
<cfif structKeyExists(_taffyRequest.headers, "origin")> <cfif structKeyExists(_taffyRequest.headers, "origin")>
<cfloop from="1" to="#arrayLen( local.domains )#" index="local.i"> <cfloop from="1" to="#arrayLen( local.domains )#" index="local.i">
@@ -560,11 +472,9 @@
</cfif> </cfif>
</cfif> </cfif>
<cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" /> <cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" />
<!--- Why do we parrot back these headers? See: https://github.com/atuttle/Taffy/issues/144 --->
<cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")> <cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")>
<cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" /> <cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" />
<cfelse> <cfelse>
<!--- parrot back all of the request headers to allow the request to continue (can we improve on this?) --->
<cfset local.allowedHeaders = {} /> <cfset local.allowedHeaders = {} />
<cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h"> <cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h">
<cfset local.allowedHeaders[local.h] = 1 /> <cfset local.allowedHeaders[local.h] = 1 />
@@ -577,14 +487,11 @@
</cfif> </cfif>
</cfif> </cfif>
<!--- global headers --->
<cfset addHeaders(getGlobalHeaders()) /> <cfset addHeaders(getGlobalHeaders()) />
<!---
Now we know everything we need to know to service the request. let's service it! Now we know everything we need to know to service the request. let's service it!
---> --->
<!--- ...after we let the api developer know all of the request details first... --->
<cfset m.beforeOnTaffyRequest = getTickCount() /> <cfset m.beforeOnTaffyRequest = getTickCount() />
<cfset _taffyRequest.continue = onTaffyRequest( <cfset _taffyRequest.continue = onTaffyRequest(
_taffyRequest.verb _taffyRequest.verb
@@ -599,7 +506,6 @@
<cfset m.otrTime = m.afterOnTaffyRequest - m.beforeOnTaffyRequest /> <cfset m.otrTime = m.afterOnTaffyRequest - m.beforeOnTaffyRequest />
<cfif not structKeyExists(_taffyRequest, "continue")> <cfif not structKeyExists(_taffyRequest, "continue")>
<!--- developer forgot to return true --->
<cfthrow <cfthrow
message="Error in your onTaffyRequest method" message="Error in your onTaffyRequest method"
detail="Your onTaffyRequest method returned no value. Expected: Return TRUE or call noData()/representationOf()." detail="Your onTaffyRequest method returned no value. Expected: Return TRUE or call noData()/representationOf()."
@@ -608,19 +514,14 @@
</cfif> </cfif>
<cfif isObject(_taffyRequest.continue)> <cfif isObject(_taffyRequest.continue)>
<!--- inspection complete but request has been aborted by developer; return custom response --->
<cfset _taffyRequest.result = duplicate(_taffyRequest.continue) /> <cfset _taffyRequest.result = duplicate(_taffyRequest.continue) />
<cfset structDelete(_taffyRequest, "continue")/> <cfset structDelete(_taffyRequest, "continue")/>
<cfset m.resourceTime = 0 /> <cfset m.resourceTime = 0 />
<cfelse> <cfelse>
<!--- inspection complete and request allowed by developer --->
<!--- handle requests for simulated responses --->
<cfif structKeyExists(_taffyRequest.requestArguments, application._taffy.settings.simulateKey) and _taffyRequest.requestArguments[application._taffy.settings.simulateKey] eq application._taffy.settings.simulatePassword> <cfif structKeyExists(_taffyRequest.requestArguments, application._taffy.settings.simulateKey) and _taffyRequest.requestArguments[application._taffy.settings.simulateKey] eq application._taffy.settings.simulatePassword>
<!--- is there a simulated response? --->
<cfset sampler = 'sample#_taffyRequest.method#Response' /> <cfset sampler = 'sample#_taffyRequest.method#Response' />
<cfif structKeyExists(_taffyRequest.matchDetails.metadata, sampler)> <cfif structKeyExists(_taffyRequest.matchDetails.metadata, sampler)>
<!--- get simulated response --->
<cfinvoke <cfinvoke
component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#" component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#"
method="#sampler#" method="#sampler#"
@@ -628,13 +529,10 @@
/> />
<cfset _taffyRequest.result = rep(_taffyRequest.result) /> <cfset _taffyRequest.result = rep(_taffyRequest.result) />
<cfelse> <cfelse>
<!--- no method for simulated response, so return 400 --->
<cfset _taffyRequest.result = noData().withStatus(400, "No Sample Response Available") /> <cfset _taffyRequest.result = noData().withStatus(400, "No Sample Response Available") />
</cfif> </cfif>
<cfelse> <cfelse>
<!--- send request to service --->
<cfif structKeyExists(_taffyRequest.matchDetails.methods, _taffyRequest.verb)> <cfif structKeyExists(_taffyRequest.matchDetails.methods, _taffyRequest.verb)>
<!--- check the cache before we call the resource --->
<cfset m.cacheCheckTime = getTickCount() /> <cfset m.cacheCheckTime = getTickCount() />
<cfset local.cacheKey = getCacheKey( <cfset local.cacheKey = getCacheKey(
_taffyRequest.matchDetails.beanName _taffyRequest.matchDetails.beanName
@@ -652,7 +550,6 @@
<cfelse> <cfelse>
<cfset structDelete(m, "cacheCheckTime") /> <cfset structDelete(m, "cacheCheckTime") />
</cfif> </cfif>
<!--- returns a representation-object --->
<cfset m.beforeResource = getTickCount() /> <cfset m.beforeResource = getTickCount() />
<cfinvoke <cfinvoke
component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#" component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#"
@@ -669,7 +566,6 @@
errorcode="taffy.resources.ResourceReturnsNothing" errorcode="taffy.resources.ResourceReturnsNothing"
/> />
</cfif> </cfif>
<!--- If the type returned is not an instance of baseSerializer, wrap it with a call to rep().
This way we can directly return the object instead of a serializer from resource actions. ---> This way we can directly return the object instead of a serializer from resource actions. --->
<cfif !isInstanceOf(_taffyRequest.result, "taffy.core.baseSerializer")> <cfif !isInstanceOf(_taffyRequest.result, "taffy.core.baseSerializer")>
<cfset _taffyRequest.result = rep(_taffyRequest.result) /> <cfset _taffyRequest.result = rep(_taffyRequest.result) />
@@ -681,11 +577,9 @@
</cfif> </cfif>
</cfif> </cfif>
<cfelseif NOT listFind(local.allowVerbs,_taffyRequest.verb)> <cfelseif NOT listFind(local.allowVerbs,_taffyRequest.verb)>
<!--- if the verb is not implemented, refuse the request --->
<cfheader name="ALLOW" value="#local.allowVerbs#" /> <cfheader name="ALLOW" value="#local.allowVerbs#" />
<cfset throwError(405, "Method Not Allowed") /> <cfset throwError(405, "Method Not Allowed") />
<cfelse> <cfelse>
<!--- create dummy response for cross domain OPTIONS request --->
<cfset _taffyRequest.resultHeaders = structNew() /> <cfset _taffyRequest.resultHeaders = structNew() />
<cfset _taffyRequest.statusArgs = structNew() /> <cfset _taffyRequest.statusArgs = structNew() />
<cfset _taffyRequest.statusArgs.statusCode = 200 /> <cfset _taffyRequest.statusArgs.statusCode = 200 />
@@ -694,17 +588,14 @@
</cfif> </cfif>
</cfif> </cfif>
<!--- make sure the requested mime type is available --->
<cfif not mimeSupported(_taffyRequest.returnMimeExt)> <cfif not mimeSupported(_taffyRequest.returnMimeExt)>
<cfset throwError(400, "Requested format not available (#_taffyRequest.returnMimeExt#)") /> <cfset throwError(400, "Requested format not available (#_taffyRequest.returnMimeExt#)") />
</cfif> </cfif>
<cfif structKeyExists(_taffyRequest,'result')> <cfif structKeyExists(_taffyRequest,'result')>
<!--- get status code --->
<cfset _taffyRequest.statusArgs = structNew() /> <cfset _taffyRequest.statusArgs = structNew() />
<cfset _taffyRequest.statusArgs.statusCode = _taffyRequest.result.getStatus() /> <cfset _taffyRequest.statusArgs.statusCode = _taffyRequest.result.getStatus() />
<cfset _taffyRequest.statusArgs.statusText = _taffyRequest.result.getStatusText() /> <cfset _taffyRequest.statusArgs.statusText = _taffyRequest.result.getStatusText() />
<!--- get custom headers --->
<cfinvoke <cfinvoke
component="#_taffyRequest.result#" component="#_taffyRequest.result#"
method="getHeaders" method="getHeaders"
@@ -716,13 +607,10 @@
<cfcontent reset="true" type="#getReturnMimeAsHeader(_taffyRequest.returnMimeExt)#; charset=utf-8" /> <cfcontent reset="true" type="#getReturnMimeAsHeader(_taffyRequest.returnMimeExt)#; charset=utf-8" />
<cfheader statuscode="#_taffyRequest.statusArgs.statusCode#" statustext="#_taffyRequest.statusArgs.statusText#" /> <cfheader statuscode="#_taffyRequest.statusArgs.statusCode#" statustext="#_taffyRequest.statusArgs.statusText#" />
<!--- headers --->
<cfset addHeaders(_taffyRequest.resultHeaders) /> <cfset addHeaders(_taffyRequest.resultHeaders) />
<!--- add ALLOW header for current resource, which describes available verbs --->
<cfheader name="ALLOW" value="#local.allowVerbs#" /> <cfheader name="ALLOW" value="#local.allowVerbs#" />
<!--- metrics headers that should always apply --->
<cfheader name="X-TIME-IN-PARSE" value="#m.parseTime#" /> <cfheader name="X-TIME-IN-PARSE" value="#m.parseTime#" />
<cfheader name="X-TIME-IN-ONTAFFYREQUEST" value="#m.otrTime#" /> <cfheader name="X-TIME-IN-ONTAFFYREQUEST" value="#m.otrTime#" />
<cfif structKeyExists(m, "resourceTime")> <cfif structKeyExists(m, "resourceTime")>
@@ -745,7 +633,6 @@
<cfset local.exposeHeaderList = listAppend(local.exposeHeaderList, "Etag") /> <cfset local.exposeHeaderList = listAppend(local.exposeHeaderList, "Etag") />
</cfif> </cfif>
<cfloop list="#local.exposeHeaderList#" index="local.exposeHeader"> <cfloop list="#local.exposeHeaderList#" index="local.exposeHeader">
<!--- filter out default simple response headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers --->
<cfif not listFindNoCase("Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma", local.exposeHeader)> <cfif not listFindNoCase("Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma", local.exposeHeader)>
<cfset local.exposeHeaderValue = listAppend(local.exposeHeaderValue, local.exposeHeader) /> <cfset local.exposeHeaderValue = listAppend(local.exposeHeaderValue, local.exposeHeader) />
</cfif> </cfif>
@@ -755,13 +642,11 @@
</cfif> </cfif>
</cfif> </cfif>
<!--- result data --->
<cfif structKeyExists(_taffyRequest,'result')> <cfif structKeyExists(_taffyRequest,'result')>
<cfset _taffyRequest.resultType = _taffyRequest.result.getType() /> <cfset _taffyRequest.resultType = _taffyRequest.result.getType() />
<cfset local.resultSerialized = '' /> <cfset local.resultSerialized = '' />
<cfif _taffyRequest.resultType eq "textual"> <cfif _taffyRequest.resultType eq "textual">
<!--- serialize the representation's data into the requested mime type --->
<cfset _taffyRequest.metrics.beforeSerialize = getTickCount() /> <cfset _taffyRequest.metrics.beforeSerialize = getTickCount() />
<cfinvoke <cfinvoke
component="#_taffyRequest.result#" component="#_taffyRequest.result#"
@@ -772,16 +657,12 @@
<cfset m.serializeTime = m.afterSerialize - m.beforeSerialize /> <cfset m.serializeTime = m.afterSerialize - m.beforeSerialize />
<cfheader name="X-TIME-IN-SERIALIZE" value="#m.serializeTime#" /> <cfheader name="X-TIME-IN-SERIALIZE" value="#m.serializeTime#" />
<!--- apply jsonp wrapper if requested --->
<cfif structKeyExists(_taffyRequest, "jsonpCallback")> <cfif structKeyExists(_taffyRequest, "jsonpCallback")>
<cfset _taffyRequest.resultSerialized = _taffyRequest.jsonpCallback & "(" & _taffyRequest.resultSerialized & ");" /> <cfset _taffyRequest.resultSerialized = _taffyRequest.jsonpCallback & "(" & _taffyRequest.resultSerialized & ");" />
</cfif> </cfif>
<!--- don't return data if etags are enabled and the data hasn't changed --->
<cfif application._taffy.settings.useEtags and _taffyRequest.verb eq "GET"> <cfif application._taffy.settings.useEtags and _taffyRequest.verb eq "GET">
<!--- etag values are quoted per: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag --->
<cfif structKeyExists(server, "lucee")> <cfif structKeyExists(server, "lucee")>
<!--- hashCode() will not work for lucee, see issue #354 --->
<cfset _taffyRequest.serverEtag = '"' & hash(_taffyRequest.resultSerialized) & '"' /> <cfset _taffyRequest.serverEtag = '"' & hash(_taffyRequest.resultSerialized) & '"' />
<cfelse> <cfelse>
<cfset _taffyRequest.serverEtag = '"' & _taffyRequest.result.getData().hashCode() & '"' /> <cfset _taffyRequest.serverEtag = '"' & _taffyRequest.result.getData().hashCode() & '"' />
@@ -812,7 +693,6 @@
<cfif _taffyRequest.resultSerialized neq ('"' & '"')> <cfif _taffyRequest.resultSerialized neq ('"' & '"')>
<cfset local.resultSerialized = _taffyRequest.resultSerialized /> <cfset local.resultSerialized = _taffyRequest.resultSerialized />
</cfif> </cfif>
<!--- debug output --->
<cfif structKeyExists(url, application._taffy.settings.debugKey)> <cfif structKeyExists(url, application._taffy.settings.debugKey)>
<cfset local.debug = true /> <cfset local.debug = true />
</cfif> </cfif>
@@ -848,7 +728,6 @@
<cfset local.result = _taffyRequest.result.getData() /> <cfset local.result = _taffyRequest.result.getData() />
</cfif> </cfif>
<!--- ...after the service has finished... --->
<cfset m.beforeOnTaffyRequestEnd = getTickCount() /> <cfset m.beforeOnTaffyRequestEnd = getTickCount() />
<cfset onTaffyRequestEnd( <cfset onTaffyRequestEnd(
_taffyRequest.verb _taffyRequest.verb
@@ -868,7 +747,6 @@
<cfif len(trim(local.resultSerialized))> <cfif len(trim(local.resultSerialized))>
<cfoutput>#local.resultSerialized#</cfoutput> <cfoutput>#local.resultSerialized#</cfoutput>
</cfif> </cfif>
<!--- debug output --->
<cfif local.debug> <cfif local.debug>
<cfoutput><h3>Request Details:</h3><cfdump var="#_taffyRequest#"></cfoutput> <cfoutput><h3>Request Details:</h3><cfdump var="#_taffyRequest#"></cfoutput>
</cfif> </cfif>
@@ -877,13 +755,7 @@
</cffunction> </cffunction>
<!--- <cffunction name="checkForLocalDebug">
<cfreturn fileExists("#GetDirectoryFromPath(GetCurrentTemplatePath())#/etc/local-debug")>
</cffunction> --->
</cfcomponent> </cfcomponent>
``` ```
## v1/index.cfm ## v1/index.cfm
@@ -1414,123 +1286,60 @@ component
## v1/lib/TokenGenerator.cfc ## v1/lib/TokenGenerator.cfc
```cfml ```cfml
component /*https://www.bennadel.com/blog/2976-trying-to-generate-cryptographically-strong-random-tokens-in-coldfusion.htm*/ component
output = false output = false
hint = "Генерирует случайные токены с помощью Java SecureRandom." hint = "Генерирует случайные токены с помощью Java SecureRandom."
{ {
/**
* Инициализирует генератор токенов.
*
* @output false
*/
public any function init() { public any function init() {
// Реализация генератора, используемая для построения случайных токенов.
// SHA1PRNG не является алгоритмом по умолчанию во всех реализациях JVM,
// поэтому он задается явно для предсказуемого поведения.
generator = createObject( "java", "java.security.SecureRandom" ) generator = createObject( "java", "java.security.SecureRandom" )
.getInstance( .getInstance(
javaCast( "string", "SHA1PRNG" ), javaCast( "string", "SHA1PRNG" ),
javaCast( "string", "SUN" ) javaCast( "string", "SUN" )
) )
; ;
// После инициализации нужно сгенерировать случайный байт,
// чтобы генератор сам засеялся через общий источник seed.
// Это может блокироваться до накопления достаточной энтропии,
// поэтому операция выполняется при инициализации, а не при первом использовании.
generator.nextBytes( charsetDecode( " ", "utf-8" ) ); generator.nextBytes( charsetDecode( " ", "utf-8" ) );
// Момент следующего пересева генератора.
// Это снижает риск слишком долгой работы с одним и тем же seed.
reseedAt = getNextReseedAt(); reseedAt = getNextReseedAt();
return( this ); return( this );
} }
// Публичные методы.
/**
* Генерирует криптографически стойкий токен из заданного числа случайных байтов.
* Байт-массив кодируется в форму, пригодную для использования в URL.
*
* @byteCount Количество случайных байтов для генерации токена.
* @output false
*/
public string function nextToken( numeric byteCount = 32 ) { public string function nextToken( numeric byteCount = 32 ) {
// Проверяем, нужен ли пересев генератора.
if ( now() >= reseedAt ) { if ( now() >= reseedAt ) {
// Пересев выполняется под lock, чтобы уменьшить гонки между потоками.
// Если lock не получен вовремя, поток продолжает работу с текущим состоянием генератора.
lock lock
name = "TokenGenerator.reseedCheck" name = "TokenGenerator.reseedCheck"
type = "exclusive" type = "exclusive"
timeout = 1 timeout = 1
throwOnTimeout = false throwOnTimeout = false
{ {
// Повторная проверка внутри lock: другой поток мог уже обновить seed.
if ( now() >= reseedAt ) { if ( now() >= reseedAt ) {
reseedAt = getNextReseedAt(); reseedAt = getNextReseedAt();
// Новый seed добавляется к уже существующему внутреннему состоянию генератора.
generator.setSeed( generator.generateSeed( javaCast( "int", 32 ) ) ); generator.setSeed( generator.generateSeed( javaCast( "int", 32 ) ) );
} }
} }
} }
// Создаем буфер байтов, в который будут записаны случайные значения.
var byteBuffer = charsetDecode( repeatString( " ", byteCount ), "utf-8" ); var byteBuffer = charsetDecode( repeatString( " ", byteCount ), "utf-8" );
generator.nextBytes( byteBuffer ); generator.nextBytes( byteBuffer );
return( encodeBytes( byteBuffer ) ); return( encodeBytes( byteBuffer ) );
} }
// Приватные методы.
/**
* Кодирует массив байтов в строку токена.
*
* @bytes Кодируемый массив байтов.
* @output false
*/
private string function encodeBytes( required binary bytes ) { private string function encodeBytes( required binary bytes ) {
var token = binaryEncode( bytes, "base64" ); // *** вот поэтому длина отличается от заявленной var token = binaryEncode( bytes, "base64" );
// мы хотим убрать все спецсимволы, потому что мы используем токен в качестве псевдослучайного суффикса
token = replace( token, "+", "a", "all" ); token = replace( token, "+", "a", "all" );
token = replace( token, "/", "b", "all" ); token = replace( token, "/", "b", "all" );
token = replace( token, "=", "c", "all" ); token = replace( token, "=", "c", "all" );
return( token ); return( token );
} }
/**
* Вычисляет время следующего пересева генератора.
*
* @output false
*/
private date function getNextReseedAt() { private date function getNextReseedAt() {
return( dateAdd( "h", 1, now() ) ); return( dateAdd( "h", 1, now() ) );
} }
} }
@@ -1822,7 +1631,6 @@ component {
} }
} }
``` ```
## v1/lib/expression_parser.cfc ## v1/lib/expression_parser.cfc
@@ -2116,7 +1924,6 @@ component {
</cfif> </cfif>
</cfif> </cfif>
``` ```
## v1/lib/filter_build.cfm ## v1/lib/filter_build.cfm
@@ -2184,7 +1991,6 @@ component {
</cfdefaultcase> </cfdefaultcase>
</cfswitch> </cfswitch>
</cffunction> </cffunction>
``` ```
## v1/lib/index.cfm ## v1/lib/index.cfm
@@ -2458,7 +2264,6 @@ component {
} }
} }
``` ```
## v1/lib/notifier.cfc ## v1/lib/notifier.cfc
@@ -3018,7 +2823,6 @@ component {
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
svc_id={prefix="r", type="integer"}, svc_id={prefix="r", type="integer"},
resource_realm_id={prefix="r", type="integer"}, resource_realm_id={prefix="r", type="integer"},
@@ -3037,11 +2841,9 @@ component {
<cfset local={}/> <cfset local={}/>
<!---разбор и проверка параметров запроса--->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "svcId", "integer")/> <cfset this.helper.validateField(arguments, "svcId", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -3165,8 +2967,8 @@ component {
<cfargument name="bookmarkUid" type="string" required=true hint="type:guid"/> <cfargument name="bookmarkUid" type="string" required=true hint="type:guid"/>
<cfargument name="bookmark" type="string" required=false hint="type:string"/> <cfargument name="bookmark" type="string" required=false hint="type:string"/>
<cfargument name="descr" type="string" required=false hint="type:string (no cleanup here!)"/> <cfargument name="descr" type="string" required=false hint="type:string (no cleanup here!)"/>
<cfargument name="url" type="string" required=false hint="type:string description: url"/><!--- чистить против XSS ---> <cfargument name="url" type="string" required=false hint="type:string description: url"/>
<cfargument name="iconUrl" type="string" required=false hint="type:string description: url иконки"/><!--- чистить против XSS ---> <cfargument name="iconUrl" type="string" required=false hint="type:string description: url иконки"/>
<cfargument name="sort" type="string" required=false hint="type:integer description: целое число для сортировки"/> <cfargument name="sort" type="string" required=false hint="type:integer description: целое число для сортировки"/>
<cfargument name="isEnabled" type="string" required=false hint="type:boolean description: вкл"/> <cfargument name="isEnabled" type="string" required=false hint="type:boolean description: вкл"/>
@@ -3226,7 +3028,6 @@ component {
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
bookmark_uid={prefix="b", type="guid"}, bookmark_uid={prefix="b", type="guid"},
dt_created={prefix="b", type="date"}, dt_created={prefix="b", type="date"},
@@ -3241,8 +3042,6 @@ component {
} }
/> />
<!--- предполагается показывать букмарки только "настоящим" клиентам, поэтому используем contragent_id как признак своих--->
<cffunction name="get" hint="Список букмарков"> <cffunction name="get" hint="Список букмарков">
<cfargument name="pageSize" type="string" hint="type:integer" default="100"/> <cfargument name="pageSize" type="string" hint="type:integer" default="100"/>
<cfargument name="page" type="string" hint="type:integer" default="1"/> <cfargument name="page" type="string" hint="type:integer" default="1"/>
@@ -3252,12 +3051,10 @@ component {
<cftry> <cftry>
<!---разбор и проверка параметров запроса--->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/> <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> <cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -3289,7 +3086,7 @@ component {
from bookmark b from bookmark b
where 1=1 <m:filter_build filter=#filter#/> where 1=1 <m:filter_build filter=#filter#/>
AND (b.contragent_id = <cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.contragentId# null=#!isNumeric(arguments.contragentId)#/> AND (b.contragent_id = <cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.contragentId# null=#!isNumeric(arguments.contragentId)#/>
OR b.contragent_id=-1)/*общая букмарка*/ OR b.contragent_id=-1)
order by b.contragent_id desc, b.sort asc order by b.contragent_id desc, b.sort asc
limit #maxrows# limit #maxrows#
</cfquery> </cfquery>
@@ -3329,10 +3126,10 @@ component {
<cffunction name="post" hint="Создание нового букмарка."> <cffunction name="post" hint="Создание нового букмарка.">
<cfargument name="bookmark" type="string" required=true hint="type:string description: заголовок"/><!--- чистить против XSS ---> <cfargument name="bookmark" type="string" required=true hint="type:string description: заголовок"/>
<cfargument name="descr" type="string" required=false default="" hint="type:boolean description: пользовательское примечание"/> <cfargument name="descr" type="string" required=false default="" hint="type:boolean description: пользовательское примечание"/>
<cfargument name="url" type="string" required=false default="" hint="type:string description: url"/><!--- чистить против XSS ---> <cfargument name="url" type="string" required=false default="" hint="type:string description: url"/>
<cfargument name="iconUrl" type="string" required=false default="" hint="type:string description: url иконки"/><!--- чистить против XSS ---> <cfargument name="iconUrl" type="string" required=false default="" hint="type:string description: url иконки"/>
<cfargument name="sort" type="string" required=false default="100" hint="type:integer description: целое число для сортировки"/> <cfargument name="sort" type="string" required=false default="100" hint="type:integer description: целое число для сортировки"/>
<cfargument name="isEnabled" type="string" required=false default="1" hint="type:boolean description: вкл"/> <cfargument name="isEnabled" type="string" required=false default="1" hint="type:boolean description: вкл"/>
@@ -3588,7 +3385,6 @@ component {
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!--- Спецификация полей, пригодных для фильтрации и сортировки --->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
service_param_id={prefix="p", type="cf_sql_integer"} service_param_id={prefix="p", type="cf_sql_integer"}
,service_id={prefix="p", type="cf_sql_integer"} ,service_id={prefix="p", type="cf_sql_integer"}
@@ -3633,8 +3429,6 @@ component {
} }
/> />
<!--- предполагается показывать букмарки только "настоящим" клиентам, поэтому используем contragent_id как признак своих--->
<cffunction name="get" hint="Каталог конкретных услуг"> <cffunction name="get" hint="Каталог конкретных услуг">
<cfargument name="pageSize" type="string" hint="type:integer" default="100"/> <cfargument name="pageSize" type="string" hint="type:integer" default="100"/>
<cfargument name="page" type="string" hint="type:integer" default="1"/> <cfargument name="page" type="string" hint="type:integer" default="1"/>
@@ -3644,12 +3438,10 @@ component {
<cftry> <cftry>
<!--- Разбор и проверка параметров запроса --->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/> <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> <cfset this.helper.validateField(arguments, "page", "integer")/>
<!--- Поля вне спецификации игнорируются, чтобы не добавлять отдельные исключения для orderBy и похожих параметров. --->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -7242,7 +7034,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cffunction> </cffunction>
</cfcomponent> </cfcomponent>
``` ```
## v1/resources/instance_operation_ls.cfc ## v1/resources/instance_operation_ls.cfc
@@ -7609,7 +7400,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
).withStatus(404)/> ).withStatus(404)/>
</cfif> </cfif>
<!--- Проверяем наличие запущенных операций на данном инстансе --->
<cfquery name="local.qCheckStarted" result="local.result"> <cfquery name="local.qCheckStarted" result="local.result">
select io.dt_submit, io.dt_finish, io.dt_start, io.submit_result, io.status_url select io.dt_submit, io.dt_finish, io.dt_start, io.submit_result, io.status_url
from instance_operation io from instance_operation io
@@ -7643,10 +7433,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset local.cfsParamChecker = CreateObject("component", "instance_operation_cfs_param_ls")/> <cfset local.cfsParamChecker = CreateObject("component", "instance_operation_cfs_param_ls")/>
<cfloop query=#local.qCheckCfsParams#> <cfloop query=#local.qCheckCfsParams#>
<cfif (local.qCheckCfsParams.is_required GT 0 AND NOT len(local.qCheckCfsParams.instance_operation_cfs_param_uid) GT 0)><!--- check CFS param existence ---> <cfif (local.qCheckCfsParams.is_required GT 0 AND NOT len(local.qCheckCfsParams.instance_operation_cfs_param_uid) GT 0)>
<cfreturn representationOf(this.helper.formatMessage("required CFS parameter #local.qCheckCfsParams.svc_operation_cfs_param# (#local.qCheckCfsParams.svc_operation_cfs_param_id#) is missing", "Missing required CFS parameter")).withStatus(422)/> <cfreturn representationOf(this.helper.formatMessage("required CFS parameter #local.qCheckCfsParams.svc_operation_cfs_param# (#local.qCheckCfsParams.svc_operation_cfs_param_id#) is missing", "Missing required CFS parameter")).withStatus(422)/>
<cfelse><!--- validate CFS param---> <cfelse>
<cfif isValid("guid",qCheckCfsParams.instance_operation_cfs_param_uid)><!--- довольно неуклюжий фикс ошибки для нового параметра. Можно было подставить 00000000-0000-0000-0000-000000000000, все равно дальше подставляется ---> <cfif isValid("guid",qCheckCfsParams.instance_operation_cfs_param_uid)>
<cfset local.cfsParamChecker.checkParam(local.qCheckCfsParams.svc_operation_cfs_param_id, qCheckCfsParams.param_value, arguments.usrId, qCheckCfsParams.instance_operation_cfs_param_uid)/> <cfset local.cfsParamChecker.checkParam(local.qCheckCfsParams.svc_operation_cfs_param_id, qCheckCfsParams.param_value, arguments.usrId, qCheckCfsParams.instance_operation_cfs_param_uid)/>
<cfelse> <cfelse>
<cfset local.cfsParamChecker.checkParam(local.qCheckCfsParams.svc_operation_cfs_param_id, qCheckCfsParams.param_value, arguments.usrId)/> <cfset local.cfsParamChecker.checkParam(local.qCheckCfsParams.svc_operation_cfs_param_id, qCheckCfsParams.param_value, arguments.usrId)/>
@@ -7719,12 +7509,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
join svc_operation_cfs_param socp on (iocp.svc_operation_cfs_param_id=socp.svc_operation_cfs_param_id) join svc_operation_cfs_param socp on (iocp.svc_operation_cfs_param_id=socp.svc_operation_cfs_param_id)
join svc_operation_param sop on (socp.svc_operation_id=sop.svc_operation_id AND socp.svc_operation_cfs_param=sop.svc_operation_param) join svc_operation_param sop on (socp.svc_operation_id=sop.svc_operation_id AND socp.svc_operation_cfs_param=sop.svc_operation_param)
where iocp.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationUid#"/> where iocp.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationUid#"/>
/*PostgreSQL specific*/
ON CONFLICT (instance_operation_uid,param) ON CONFLICT (instance_operation_uid,param)
DO UPDATE SET param_value = EXCLUDED.param_value; DO UPDATE SET param_value = EXCLUDED.param_value;
</cfquery> </cfquery>
<!--- так яснее, но можно было все в один запрос --->
<cfquery name="qWriteRfsParamsFromDefaults"> <cfquery name="qWriteRfsParamsFromDefaults">
insert into instance_operation_param (instance_operation_uid,param,param_value) insert into instance_operation_param (instance_operation_uid,param,param_value)
select select
@@ -7732,8 +7520,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
,sop.svc_operation_param ,sop.svc_operation_param
,sop.default_value ,sop.default_value
from svc_operation_param sop from svc_operation_param sop
where sop.svc_operation_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qSvcOperation.svc_operation_id#"/> AND length(sop.default_value)>0 /***null will be better*/ where sop.svc_operation_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qSvcOperation.svc_operation_id#"/> AND length(sop.default_value)>0
/*PostgreSQL specific*/
ON CONFLICT (instance_operation_uid,param) ON CONFLICT (instance_operation_uid,param)
DO NOTHING; DO NOTHING;
</cfquery> </cfquery>
@@ -7776,7 +7563,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset var submit_url="#qInstanceOperation.url_prefix##qInstanceOperation.version##qInstanceOperation.url_suffix#"/> <cfset var submit_url="#qInstanceOperation.url_prefix##qInstanceOperation.version##qInstanceOperation.url_suffix#"/>
</cfif> </cfif>
<!--- *** тут нужно бы указать, кто запустил операцию --->
<cfquery name="qMarkOperationStart"> <cfquery name="qMarkOperationStart">
update instance_operation update instance_operation
set dt_submit=<cfqueryparam cfsqltype="cf_sql_timestamp" value=#now()#/> set dt_submit=<cfqueryparam cfsqltype="cf_sql_timestamp" value=#now()#/>
@@ -7863,7 +7649,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.param#"/> ,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.param#"/>
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.val#"/> ,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.val#"/>
) )
/*PostgreSQL specific*/
ON CONFLICT (instance_operation_uid,param) ON CONFLICT (instance_operation_uid,param)
DO UPDATE SET param_value = EXCLUDED.param_value; DO UPDATE SET param_value = EXCLUDED.param_value;
</cfquery> </cfquery>
@@ -7985,7 +7770,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where io.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationUid#" null=#!isValid("guid",arguments.instanceOperationUid)#/> where io.instance_operation_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationUid#" null=#!isValid("guid",arguments.instanceOperationUid)#/>
</cfquery> </cfquery>
<!--- проверяем доступ к RFS параметру resourceRealm данной операции --->
<cfquery name="local.qCheckResourceRealmAccess"> <cfquery name="local.qCheckResourceRealmAccess">
select r.resource_realm_id, r.resource_realm, iop.param, iop.param_value, a.contract_id, a.is_enabled select r.resource_realm_id, r.resource_realm, iop.param, iop.param_value, a.contract_id, a.is_enabled
from resource_realm r from resource_realm r
@@ -8031,7 +7815,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cffunction> </cffunction>
</cfcomponent> </cfcomponent>
``` ```
## v1/resources/notification_ls.cfc ## v1/resources/notification_ls.cfc
@@ -8044,7 +7827,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
dt_created={prefix="n", type="date"}, dt_created={prefix="n", type="date"},
dt_updated={prefix="n", type="date"}, dt_updated={prefix="n", type="date"},
@@ -8056,9 +7838,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
external_code={prefix="n", type="string"} external_code={prefix="n", type="string"}
} }
/> />
<cffunction name="get" hint="Список уведомлений"> <cffunction name="get" hint="Список уведомлений">
<cfargument name="pageSize" type="string" hint="type:integer" default="100"/> <cfargument name="pageSize" type="string" hint="type:integer" default="100"/>
<cfargument name="page" type="string" hint="type:integer" default="1"/> <cfargument name="page" type="string" hint="type:integer" default="1"/>
@@ -8067,12 +7846,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cftry> <cftry>
<cfset local={}/> <cfset local={}/>
<!---разбор и проверка параметров запроса--->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/> <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> <cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -8176,7 +7953,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cffunction> </cffunction>
</cfcomponent> </cfcomponent>
``` ```
## v1/resources/resource_realm.cfc ## v1/resources/resource_realm.cfc
@@ -8269,7 +8045,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
resource_realm_id={prefix="r", type="integer"}, resource_realm_id={prefix="r", type="integer"},
resource_realm_type_id={prefix="r", type="integer"}, resource_realm_type_id={prefix="r", type="integer"},
@@ -8288,12 +8063,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset local={}/> <cfset local={}/>
<!---разбор и проверка параметров запроса--->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/> <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> <cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -8333,7 +8106,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
OR a.contract_id=0) OR a.contract_id=0)
AND a.is_enabled) AND a.is_enabled)
where 1=1 <m:filter_build filter=#filter#/> where 1=1 <m:filter_build filter=#filter#/>
order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/><!---no sort length limit---> order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/>
limit #maxrows# limit #maxrows#
</cfquery> </cfquery>
@@ -8382,7 +8155,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/> <cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent> </cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={ <cfset this.fieldsSpec={
resource_realm_type_id={prefix="r", type="integer"}, resource_realm_type_id={prefix="r", type="integer"},
resource_realm_type={prefix="r", type="string"} resource_realm_type={prefix="r", type="string"}
@@ -8397,12 +8169,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset local={}/> <cfset local={}/>
<!---разбор и проверка параметров запроса--->
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/> <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> <cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/> <cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
@@ -8427,7 +8197,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</m:field_set> </m:field_set>
from resource_realm_type t from resource_realm_type t
where 1=1 <m:filter_build filter=#filter#/> where 1=1 <m:filter_build filter=#filter#/>
order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/><!---no sort length limit---> order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/>
limit #maxrows# limit #maxrows#
</cfquery> </cfquery>
@@ -9193,32 +8963,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcomponent> </cfcomponent>
``` ```
## v1/resources/svc_operation_cfs_subparam_compute.cfc ## v1/resources/svc_operation_cfs_subparam_compute.cfc
```cfml ```cfml
<cfcomponent extends="taffy.core.resource" taffy:uri="/svcOperationCfsSubparams/compute/{svcOperationCfsSubparamId}" hint="вычисляет выражение в поле expression"> <cfcomponent extends="taffy.core.resource" taffy:uri="/svcOperationCfsSubparams/compute/{svcOperationCfsSubparamId}" hint="вычисляет выражение в поле expression">
<!---
вычисляет выражение в поле expression
для этого нужен контекст - как минимум, сохраненные параметры, но это не работает при создании нового инстанса,
поэтому нужно инициализировать контекст и набивать его всей необходимой информацией
что особенно важно для зависимых параметров - нам нужно положить в контекст все параметры, от которых мы зависим
чтобы знать, какие параметры передавать - конструкцией для параметра предусмотрено поле depends_on_params (надо будет его сделать вычисляемым), а для субпараметра пока не
--->
<!--- Все методы неявно получают
arguments.usrId
arguments.contragentId
arguments.contractId
arguments.specificationId
arguments.requestArguments.usrId=usrCustomerInfo.usrId; //Integer!
arguments.requestArguments.contragentId=usrCustomerInfo.contragentId; //Integer
arguments.requestArguments.contractId=usrCustomerInfo.contractId; //Integer
arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //Integer
--->
<cfsilent> <cfsilent>
<cfimport prefix="m" taglib="../lib"/> <cfimport prefix="m" taglib="../lib"/>
@@ -9227,17 +8977,13 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cffunction name="get" hint="вычисление возможных значений cубпараметра CFS параметра операции, в текущем контексте"> <cffunction name="get" hint="вычисление возможных значений cубпараметра CFS параметра операции, в текущем контексте">
<!--- <cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/> а вот ни фига--->
<!--- *** Важно: мы должны уметь посчитать параметр, если он не сохранен, то есть мы знаем его класс, а не инстанс --->
<!--- Вероятно, нам не очень нужно вычислять набор значений для инстанса параметра, мы всегда знаем класс --->
<!--- контест: --->
<cfargument name="svcOperationCfsSubparamId" type="string" required=true hint="type:integer"/> <cfargument name="svcOperationCfsSubparamId" type="string" required=true hint="type:integer"/>
<cfargument name="keys" type="string" default={} hint="type:json keys we depend on: of the param uid itself, or operation uid, instance uid, service operation cfs param - if not all entities intstantiated yet"/> <cfargument name="keys" type="string" default={} hint="type:json keys we depend on: of the param uid itself, or operation uid, instance uid, service operation cfs param - if not all entities intstantiated yet"/>
<cfargument name="params" type="string" default="{}" hint="type:json any posted parameters we depend on"/> <cfargument name="params" type="string" default="{}" hint="type:json any posted parameters we depend on"/>
<cftry> <cftry>
<cfset this.helper.validateField(arguments, "svcOperationCfsSubparamId", "integer")/> <cfset this.helper.validateField(arguments, "svcOperationCfsSubparamId", "integer")/>
<cfset this.helper.validateField(arguments, "keys", "json")/> <!--- *** может, не лучшая идея передавать ключ в контексте ---> <cfset this.helper.validateField(arguments, "keys", "json")/>
<cfset this.helper.validateField(arguments, "params", "json")/> <cfset this.helper.validateField(arguments, "params", "json")/>
<cfcatch type="invalidParamValue"> <cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/> <cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
@@ -9245,12 +8991,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cftry> </cftry>
<cfset local={}/> <cfset local={}/>
<cfset "local.expression" = ""/> <!--- нет смысла выносить это в поля класса, оно нужно только локально ---> <cfset "local.expression" = ""/>
<cfset "local.calculatedValueList" = ""/> <cfset "local.calculatedValueList" = ""/>
<cfset "local.keys" = deserializeJson(arguments.keys)/> <cfset "local.keys" = deserializeJson(arguments.keys)/>
<cfset "local.params" = deserializeJson(arguments.params)/> <cfset "local.params" = deserializeJson(arguments.params)/>
<cfset "local.keys.svcOperationCfsSubparamId" = arguments.svcOperationCfsSubparamId/> <!--- *** сомнительное дело гонять аргументы туда-сюда ---><!--- *** по-моему, не используется, поэтому достанем svcOperationCfsParamId прямо тут ---> <cfset "local.keys.svcOperationCfsSubparamId" = arguments.svcOperationCfsSubparamId/>
<cfquery name="local.qCfsSubparam" result="local.result"> <cfquery name="local.qCfsSubparam" result="local.result">
select select
@@ -9272,13 +9018,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset "local.expression"=local.qCfsSubparam.expression/> <cfset "local.expression"=local.qCfsSubparam.expression/>
<cfset "local.keys.svcOperationCfsParamId" = local.qCfsSubparam.svc_operation_cfs_param_id/> <cfset "local.keys.svcOperationCfsParamId" = local.qCfsSubparam.svc_operation_cfs_param_id/>
<!--- *** можно обратить внимание, что мы инстанциируем не субпараметр, а параметр (цинично пользуясь их функциональной идентичностью... но некрасиво) --->
<cfif len(local.expression)> <cfif len(local.expression)>
<cfset local.calculatedValueList=createObject("component","lib.expression_parser") <cfset local.calculatedValueList=createObject("component","lib.expression_parser")
.eval( .eval(
local.expression, local.expression,
{/*context*/ {
component:"resources.instance_operation_cfs_param", /*param, not subparam*/ component:"resources.instance_operation_cfs_param",
keys:#local.keys#, keys:#local.keys#,
params:#local.params# params:#local.params#
} }
@@ -9287,16 +9032,14 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfelse> <cfelse>
<cfreturn representationOf(this.helper.formatMessage("Expression Not Found")).withStatus(404)/> <cfreturn representationOf(this.helper.formatMessage("Expression Not Found")).withStatus(404)/>
</cfif> </cfif>
<!--- Тут нам еще придется сформатировать лист в массив. Но обошлось довольно просто --->
<cfset var out=structNew("linked")/> <cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/> <cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.values" = listToArray(local.calculatedValueList)/> <cfset "out.values" = listToArray(local.calculatedValueList)/>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/> <cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) /> <cfreturn representationOf(out) />
</cffunction><!--- get ---> </cffunction>
</cfcomponent> </cfcomponent>
``` ```
## v1/resources/user.cfc ## v1/resources/user.cfc
@@ -9644,4 +9387,3 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcomponent> </cfcomponent>
``` ```
-62
View File
@@ -1,62 +0,0 @@
# Оглавление исходников svc-api-x
Дата сборки: 2026-04-29
## Прикладной слой
- health.cfm
- v1/Application.cfc
- v1/index.cfm
- v1/lib/JsonSerializer.cfc
- v1/lib/TokenGenerator.cfc
- v1/lib/encodingUtils.cfc
- v1/lib/expression_parser.cfc
- v1/lib/field.cfm
- v1/lib/field_set.cfm
- v1/lib/filter_build.cfm
- v1/lib/index.cfm
- v1/lib/jwt.cfc
- v1/lib/notifier.cfc
- v1/lib/order_build.cfm
- v1/lib/rest_api_helper.cfc
- v1/resources/available_resource_realms.cfc
- v1/resources/bookmark.cfc
- v1/resources/bookmark_ls.cfc
- v1/resources/catalog_service_ls.cfc
- v1/resources/catalog_service_param_ls.cfc
- v1/resources/err.cfc
- v1/resources/instance.cfc
- v1/resources/instance_default.cfc
- v1/resources/instance_ls.cfc
- v1/resources/instance_operation.cfc
- v1/resources/instance_operation_cfs_param.cfc
- v1/resources/instance_operation_cfs_param_ls.cfc
- v1/resources/instance_operation_cfs_subparam.cfc
- v1/resources/instance_operation_cfs_subparam_ls.cfc
- v1/resources/instance_operation_default.cfc
- v1/resources/instance_operation_ls.cfc
- v1/resources/instance_operation_run.cfc
- v1/resources/instance_operation_validate.cfc
- v1/resources/notification_ls.cfc
- v1/resources/param_value_list.cfc
- v1/resources/resource_realm.cfc
- v1/resources/resource_realm_ls.cfc
- v1/resources/resource_realm_type_ls.cfc
- v1/resources/svc.cfc
- v1/resources/svc_default.cfc
- v1/resources/svc_grouped_ls.cfc
- v1/resources/svc_ls.cfc
- v1/resources/svc_operation.cfc
- v1/resources/svc_operation_cfs_param_compute.cfc
- v1/resources/svc_operation_cfs_subparam_compute.cfc
- v1/resources/user.cfc
- v1/resources/vault_record.cfc
## Backup-слой, исключенный из основного bundle
- отсутствует
## Исключено из bundle
- v1/resources/bk/* как удаленные backup-сериализаторы
- v1/taffy/* как vendor framework, примеры и тесты
+68
View File
@@ -0,0 +1,68 @@
# Оглавление исходников svc-api-x
Дата сборки: 2026-04-30
## Прикладной слой
```text
health.cfm ........................................ 7
v1/Application.cfc ................................ 12
v1/index.cfm ...................................... 761
v1/lib/JsonSerializer.cfc .......................... 767
v1/lib/TokenGenerator.cfc .......................... 1286
v1/lib/encodingUtils.cfc ........................... 1348
v1/lib/expression_parser.cfc ....................... 1636
v1/lib/field.cfm ................................... 1795
v1/lib/field_set.cfm .............................. 1860
v1/lib/filter_build.cfm ........................... 1929
v1/lib/index.cfm .................................. 1996
v1/lib/jwt.cfc .................................... 2002
v1/lib/notifier.cfc ................................ 2269
v1/lib/order_build.cfm ............................ 2307
v1/lib/rest_api_helper.cfc ........................ 2353
v1/resources/available_resource_realms.cfc .......... 2816
v1/resources/bookmark.cfc .......................... 2910
v1/resources/bookmark_ls.cfc ....................... 3021
v1/resources/catalog_service_ls.cfc ................ 3205
v1/resources/catalog_service_param_ls.cfc .......... 3378
v1/resources/err.cfc ............................... 3574
v1/resources/instance.cfc .......................... 3589
v1/resources/instance_default.cfc .................. 4120
v1/resources/instance_ls.cfc ....................... 4176
v1/resources/instance_operation.cfc ............... 4725
v1/resources/instance_operation_cfs_param.cfc ...... 5026
v1/resources/instance_operation_cfs_param_ls.cfc ... 6004
v1/resources/instance_operation_cfs_subparam.cfc ... 6449
v1/resources/instance_operation_cfs_subparam_ls.cfc . 6626
v1/resources/instance_operation_default.cfc ........ 6712
v1/resources/instance_operation_ls.cfc ............. 7039
v1/resources/instance_operation_run.cfc ............ 7345
v1/resources/instance_operation_validate.cfc ....... 7791
v1/resources/notification_ls.cfc ................... 7820
v1/resources/param_value_list.cfc .................. 7928
v1/resources/resource_realm.cfc .................... 7958
v1/resources/resource_realm_ls.cfc ................. 8038
v1/resources/resource_realm_type_ls.cfc ............ 8148
v1/resources/svc.cfc ................................ 8239
v1/resources/svc_default.cfc ........................ 8322
v1/resources/svc_grouped_ls.cfc .................... 8367
v1/resources/svc_ls.cfc ............................ 8541
v1/resources/svc_operation.cfc ..................... 8770
v1/resources/svc_operation_cfs_param_compute.cfc ... 8891
v1/resources/svc_operation_cfs_subparam_compute.cfc . 8968
v1/resources/user.cfc .............................. 9045
v1/resources/vault_record.cfc ...................... 9140
```
## Backup-слой, исключенный из основного bundle
```text
отсутствует
```
## Исключено из bundle
```text
v1/resources/bk/* .......................... удаленные backup-сериализаторы
v1/taffy/* ................................ vendor framework, примеры и тесты
```