20 changed files with 352 additions and 1640 deletions
+91
View File
@@ -0,0 +1,91 @@
"""
Пересобирает results/source-bundle.md и results/source-toc.md
из очищенных исходников v1/ (без taffy и без backup-каталогов).
Запуск:
python3 build_bundle.py
Что делает:
1. Обходит v1/ рекурсивно, собирает все .cfc и .cfm файлы
(исключая taffy/ и bk/)
2. Формирует results/source-bundle.md — один большой MD,
каждый файл как раздел ## filepath + код в блоке ```cfml
3. Формирует results/source-toc.md — оглавление с номерами строк
в bundle (не страниц)
Настройки:
ROOT — корневая директория исходников
SKIP — папки, которые исключаются из обхода
OUT_DIR — куда писать результирующие файлы
"""
import os
import re
from datetime import date
ROOT = 'v1'
SKIP = ['taffy', 'bk']
OUT_DIR = 'results'
BUILD_DATE = date.today().isoformat()
def collect_files(root, skip):
files = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = sorted([d for d in dirnames if d not in skip])
for fname in sorted(filenames):
if fname.endswith('.cfc') or fname.endswith('.cfm'):
files.append(os.path.join(dirpath, fname))
return files
def build_bundle(files):
lines = []
lines.append(f'# Исходники svc-api-x\n')
lines.append(f'Дата сборки: {BUILD_DATE}\n')
lines.append('Прикладной слой после cleanup: v1/ без taffy и без backup-каталогов.\n')
for fpath in files:
with open(fpath, encoding='utf-8', errors='ignore') as f:
code = f.read().rstrip('\n')
lines.append(f'\n## {fpath}\n')
lines.append(f'\n```cfml\n{code}\n```\n')
return '\n'.join(lines)
def build_toc(bundle_content):
bundle_lines = bundle_content.split('\n')
toc_entries = []
for i, line in enumerate(bundle_lines, 1):
m = re.match(r'^## (.+)$', line)
if m:
toc_entries.append((m.group(1).strip(), i))
out = []
out.append(f'# Оглавление исходников svc-api-x\n')
out.append(f'Дата сборки: {BUILD_DATE}\n')
out.append('\n## Прикладной слой\n')
out.append('```text')
for rel, lineno in toc_entries:
dots = '.' * max(1, 55 - len(rel) - len(str(lineno)))
out.append(f'{rel} {dots} {lineno}')
out.append('```')
return '\n'.join(out) + '\n'
if __name__ == '__main__':
files = collect_files(ROOT, SKIP)
bundle = build_bundle(files)
bundle_path = os.path.join(OUT_DIR, 'source-bundle.md')
with open(bundle_path, 'w', encoding='utf-8') as f:
f.write(bundle)
toc = build_toc(bundle)
toc_path = os.path.join(OUT_DIR, 'source-toc.md')
with open(toc_path, 'w', encoding='utf-8') as f:
f.write(toc)
print(f'Файлов в bundle: {len(files)}')
print(f'{bundle_path} — готово')
print(f'{toc_path} — готово')
+71 -8
View File
@@ -7,9 +7,16 @@
3. Строит DOCX:
- Титульная страница (название, подзаголовок, дата)
- Оглавление (поле Word TOC — пользователь жмёт правую кнопку → Обновить поле)
- 47 разделов с кодом (заголовок файла + код моноширинным шрифтом на сером фоне)
- разделы с кодом (заголовок файла + код с подсветкой синтаксиса на сером фоне)
- Номер страницы в нижнем колонтитуле
Подсветка синтаксиса CFML (4 цвета):
синий — CF-теги (<cfif>, <cfset>, </cffunction>, ...)
зелёный — строковые значения ("...", '...')
фиолетовый — выражения в # (#variables#, #func()#)
тёмно-голубой — SQL-ключевые слова (SELECT, FROM, WHERE, JOIN, ...)
чёрный — всё остальное
Запуск:
/home/naeel/svc-api-x/.venv/bin/python build_docx.py
@@ -39,8 +46,8 @@ from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
BUNDLE_PATH = '/home/naeel/svc-api-x/results/source-bundle.md' # входной файл
OUTPUT_PATH = '/home/naeel/svc-api-x/results/svc-api-x.docx' # выходной файл
BUNDLE_PATH = 'results/source-bundle.md' # входной файл
OUTPUT_PATH = 'results/svc-api-x.docx' # выходной файл
# ---------------------------------------------------------------------------
@@ -70,7 +77,61 @@ def parse_bundle(path):
# Вспомогательные функции
# ---------------------------------------------------------------------------
def set_font(run, name='Courier New', size=9, bold=False, color=None):
# ---------------------------------------------------------------------------
# Токенизатор CFML
# ---------------------------------------------------------------------------
# Цвета подсветки (RGB)
COL_TAG = (0x1A, 0x5F, 0xB4) # синий — CF-теги
COL_STR = (0x1E, 0x7E, 0x34) # зелёный — строки
COL_VAR = (0x6F, 0x31, 0x78) # фиолетовый — #переменные#
COL_SQL = (0x0D, 0x5C, 0x73) # тёмно-голубой — SQL
COL_DEF = None # чёрный (default)
_PATTERNS = [
('tag', re.compile(
r'</cf\w+|<cf\w+|</cfscript>|<cfscript>', re.IGNORECASE)),
('var', re.compile(
r'#[^#\n]+#')),
('str', re.compile(
r'"[^"\n]*"|' + r"'[^'\n]*'")),
('sql', re.compile(
r'\b(SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|AND|OR|NOT|'
r'INSERT|INTO|UPDATE|DELETE|SET|GROUP|ORDER|BY|HAVING|LIMIT|OFFSET|'
r'AS|DISTINCT|COUNT|SUM|AVG|MAX|MIN|CAST|COALESCE|CASE|WHEN|THEN|'
r'ELSE|END|IS|NULL|IN|EXISTS|UNION|ALL)\b', re.IGNORECASE)),
]
_COLOR_MAP = {'tag': COL_TAG, 'str': COL_STR, 'var': COL_VAR,
'sql': COL_SQL, None: COL_DEF}
def tokenize_cfml(line):
"""Разбивает строку кода на список (text, color) с подсветкой CFML."""
tokens = []
pos = 0
length = len(line)
while pos < length:
best_match = None
best_kind = None
best_start = length
for kind, pat in _PATTERNS:
m = pat.search(line, pos)
if m and m.start() < best_start:
best_match = m
best_kind = kind
best_start = m.start()
if best_match is None:
tokens.append((line[pos:], COL_DEF))
break
if best_start > pos:
tokens.append((line[pos:best_start], COL_DEF))
tokens.append((best_match.group(0), _COLOR_MAP[best_kind]))
pos = best_match.end()
return tokens
def set_font(run, name='Courier New', size=8, bold=False, color=None):
run.font.name = name
run.font.size = Pt(size)
run.font.bold = bold
@@ -259,11 +320,13 @@ def build(sections):
for idx, line in enumerate(lines):
if idx > 0:
run = code_para.add_run()
br_run = code_para.add_run()
br = OxmlElement('w:br')
run._r.append(br)
run = code_para.add_run(line)
set_font(run, name='Courier New', size=8)
br_run._r.append(br)
set_font(br_run)
for text, color in tokenize_cfml(line):
run = code_para.add_run(text)
set_font(run, color=color)
doc.save(OUTPUT_PATH)
print(f'Готово: {OUTPUT_PATH}')
+116 -783
View File
File diff suppressed because it is too large Load Diff
+48 -61
View File
@@ -1,68 +1,55 @@
# Оглавление исходников svc-api-x
Дата сборки: 2026-04-30
Дата сборки: 2026-05-19
## Прикладной слой
```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, примеры и тесты
v1/Application.cfc .................................... 8
v1/index.cfm ........................................ 767
v1/lib/JsonSerializer.cfc ........................... 775
v1/lib/TokenGenerator.cfc .......................... 1296
v1/lib/encodingUtils.cfc ........................... 1360
v1/lib/expression_parser.cfc ....................... 1650
v1/lib/field.cfm ................................... 1811
v1/lib/field_set.cfm ............................... 1878
v1/lib/filter_build.cfm ............................ 1949
v1/lib/index.cfm ................................... 2018
v1/lib/jwt.cfc ..................................... 2026
v1/lib/notifier.cfc ................................ 2295
v1/lib/order_build.cfm ............................. 2335
v1/lib/rest_api_helper.cfc ......................... 2383
v1/resources/available_resource_realms.cfc ......... 2848
v1/resources/bookmark.cfc .......................... 2944
v1/resources/bookmark_ls.cfc ....................... 3057
v1/resources/catalog_service_ls.cfc ................ 3243
v1/resources/catalog_service_param_ls.cfc .......... 3418
v1/resources/err.cfc ............................... 3616
v1/resources/instance.cfc .......................... 3633
v1/resources/instance_default.cfc .................. 4081
v1/resources/instance_ls.cfc ....................... 4139
v1/resources/instance_operation.cfc ................ 4577
v1/resources/instance_operation_cfs_param.cfc ...... 4880
v1/resources/instance_operation_cfs_param_ls.cfc ... 5678
v1/resources/instance_operation_cfs_subparam.cfc ... 6125
v1/resources/instance_operation_cfs_subparam_ls.cfc . 6270
v1/resources/instance_operation_default.cfc ........ 6338
v1/resources/instance_operation_ls.cfc ............. 6619
v1/resources/instance_operation_run.cfc ............ 6878
v1/resources/instance_operation_validate.cfc ....... 7326
v1/resources/notification_ls.cfc ................... 7348
v1/resources/param_value_list.cfc .................. 7458
v1/resources/resource_realm.cfc .................... 7490
v1/resources/resource_realm_ls.cfc ................. 7572
v1/resources/resource_realm_type_ls.cfc ............ 7684
v1/resources/svc.cfc ............................... 7777
v1/resources/svc_default.cfc ....................... 7844
v1/resources/svc_grouped_ls.cfc .................... 7891
v1/resources/svc_ls.cfc ............................ 8018
v1/resources/svc_operation.cfc ..................... 8211
v1/resources/svc_operation_cfs_param_compute.cfc ... 8319
v1/resources/svc_operation_cfs_subparam_compute.cfc . 8398
v1/resources/user.cfc .............................. 8477
v1/resources/vault_record.cfc ...................... 8533
```
Binary file not shown.
Binary file not shown.
+3 -88
View File
@@ -1,11 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instances/{instanceUid}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<cffunction name="get" hint="Экземпляр сервиса"><!--- *** TODO проверка принадлежности тенанту --->
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cftry>
@@ -14,9 +11,7 @@
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset var local={}/>
<cfquery name="local.qInstance" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
@@ -36,7 +31,6 @@
<m:field>v.svc</m:field>
<m:field>v.code</m:field>
<m:field>v.man
</m:field>
<m:field>e.updater_id</m:field>
<m:field>u.login as updater_login</m:field>
@@ -71,7 +65,6 @@
AND a.is_enabled)
where r.resource_realm_type_id=v.resource_realm_type_id) as resource_realm_cnt
</m:field>
<!--- <m:field>st.version</m:field> --->
</m:field_set>
from instance e
left outer join specification_item i on (e.specification_item_id=i.specification_item_id)
@@ -94,16 +87,13 @@
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
AND s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/><!--- access protection --->
</cfquery> <!--- *** правильнее при попытке доступа к чужому инстансу (НСД) выбрасывать 403, а тут будет 404, но так тоже делают, например, в gitea --->
<cfif local.qInstance.recordCount EQ 0>
<cfreturn representationOf("Instance not found or no permission to view").withStatus(404)/>
</cfif>
<!--- instanceData?.suspended
instanceData?.out?.monitoring?.allDashboards
instanceData?.params
instanceData?.out --->
<cfquery name="local.qCurrentState" result="local.result">
select
<m:field_set titleMapOut="local.currentStateTitleMap" lengthOut="local.fieldCount">
@@ -113,7 +103,6 @@
<m:field>to_char(st.dt_state, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_state</m:field>
<m:field formatter=#request.castToBool#>st.is_test</m:field>
<m:field>st.instance_operation_uid::text as instance_operation_uid</m:field>
<!--- <m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data::text as instance_data</m:field> --->
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'out' as out</m:field>
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'params' as params</m:field>
<m:field formatter=#function(x){return (deserializeJson(x))}#>st.instance_data->>'vault' as vault</m:field>
@@ -125,13 +114,8 @@
order by st.version desc
limit 1
</cfquery><!--- берем local.qInstance.instance_uid ради изоляции (access protection) --->
<cfset local.isCreated = (local.qCurrentState.recordCount GT 0)/>
<cfset local.isDeleted = (local.qCurrentState.is_deleted GT 0)/>
<!--- <cfdump var=#local.qInstance#/><cfabort/> --->
<!--- операция, создавшая текущее состояние --->
<!--- *** странноватый селект --->
<cfquery name="local.qOperation" result="local.result">
@@ -171,11 +155,7 @@
<!--- *** А тут у нас не получится, что только операции с состояниями? Или в неправильном порядке? --->
<!--- ?Добавить параметры к операции --->
<!--- Разобраться в отличии текущей от последней. Может, нужна только одна? --->
<!--- <d:field title="Операция">(select operation from instance_state st join instance_operation io on (st.instance_operation_uid=io.instance_operation_uid) where st.instance_uid=e.instance_uid order by version desc limit 1) as operation</d:field>
<!--- текущая операция. Она еще не создала стейта и не завершена --->
<d:field title="Текущая операция">(select io.operation from instance_operation io where io.instance_uid=e.instance_uid order by dt_submit desc limit 1) as current_operation</d:field> --->
<!--- *** Отработать блокировку --->
<cfquery name="local.qAvailableOperation" result="local.result">
select
@@ -204,17 +184,12 @@
</cfif>
order by so.svc_operation_id
</cfquery>
<!--- Проблема зависимостей. Чтобы их увидеть, нам нужно состояние, а его нет в реляционном виде.
Значит, придется парсить стейт
но чтобы получить все сервисы, которые зависят от данного, придется им отпарсить все стейты, что нереально
Значит, придется заняться нормализаций стейта (приведением в реляционный вид) - впрочем, ничего сильно сложного в этом нет
А есть смысл делать это в одну строну? наверно, есть - чтобы не забираться в операцию
--->
<!--- *** здесь уместна параноидальная проверка прав --->
<cfquery name="local.qDependency">
select
@@ -240,7 +215,6 @@
left outer join svc_operation so on (o.operation=so.operation AND e.service_id=so.svc_id)
left outer join svc_operation_cfs_param sop on (so.svc_operation_id=sop.svc_operation_id AND p.param=sop.svc_operation_cfs_param)
left outer join svc s on (sop.ref_svc_id=s.svc_id)
join instance r on (p.param_value=r.instance_uid::text)
left outer join specification_item si on (r.specification_item_id=si.specification_item_id)
left outer join specification sp on (si.specification_id=sp.specification_id)
@@ -250,7 +224,6 @@
AND sop.ref_svc_id > 0
order by sop.sort, p.param
</cfquery>
<!--- *** здесь уместна параноидальная проверка прав
потому что можно сослаться на чужой инстанс и увидеть какие-то лишние детали--->
<cfquery name="local.qDependentInstance">
@@ -289,14 +262,10 @@
where i.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>
)
</cfquery>
<!--- ---------------------------------------------------------------------------------- --->
<!--- маскируем секреты *** это не очень похоже на то же самое в списке, там мы просто удаляем параметры --->
<!--- *** подгоняем запрос под структуру из instance_operation_cfs_param --->
<!--- а мы тут упускаем, что может быть несколько операций create, как правило из-за сломавшихся. Но параметры мы берем из текущего стейта, и операцию возьмем оттуда же --->
<cfquery name="local.qCfsParam" result="local.result">
select
p.svc_operation_cfs_param
@@ -320,7 +289,6 @@
where svc.svc_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qInstance.service_id#" null=#!isValid('integer',qInstance.service_id)#/>
AND o.operation='create'
</cfquery>
<!--- *** сделано не так, как в instance_operation_cfs_param (а там тоже сделано так себе) --->
<cfset local.params = structNew("linked")/>
<cftry>
@@ -333,7 +301,7 @@
<cfset local.params[paramName]="********"/>
<cfelseif local.qCfsParam.data_type EQ "map" OR local.qCfsParam.data_type EQ "map-fixed" OR local.qCfsParam.data_type EQ "array-map-fixed">
<!--- Не разбираем вариант "sensitive map" --->
<!--- <cfset processMap(local.qCfsParam)/> ---><!--- query passed by reference --->
<!--- query passed by reference --->
<!--- маскируем секреты в map --->
<!--- *** сделано ненадежно, с расчетом на структуру qCfsParam (ради этого ее пришлось дополнить) --->
<!--- что характерно, мы передаем запрос (с фокусом, установленным на текущую строку) по ссылке и прямо там ее патчим --->
@@ -341,70 +309,42 @@
</cfif>
</cfif>
</cfloop>
<!--- грубо и цинично патчим запрос --->
<cfset local.qCurrentState.params = serializeJson(local.params)/>
<cfcatch type="ANY"><!--- *** <cfrethrow/>---></cfcatch>
<cfcatch type="ANY"></cfcatch>
</cftry>
<!--- ---------------------------------------------------------------------------------- --->
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.instance" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qInstance, this.helper.snake2camel
)/>
<!--- <cfset "out.instance.uptime"=(local.qOperation.is_successful GT 0) ? local.qOperation.seconds_passed : 0/> --->
<cfset "out.instance.isCreated"=local.isCreated/>
<cfset "out.instance.isDeleted"=local.qCurrentState.is_deleted GT 0/>
<!--- <cfset "out.instance.vaultData"=local.vaultData/> --->
<cfset "out.instance.state" = (local.qCurrentState.recordCount GT 0) ? this.helper.appendRecord(structNew("linked"), "", local.currentStateTitleMap, local.qCurrentState, this.helper.snake2camel) : {}/>
<cfset "out.instance.operations"=[]/>
<cfloop query=#local.qOperation#>
<cfset arrayAppend(out.instance.operations, this.helper.appendRecord(structNew("linked"), "", local.operationTitleMap, local.qOperation, this.helper.snake2camel))/>
</cfloop>
<cfset "out.instance.availableOperations"=[]/>
<cfloop query=#local.qAvailableOperation#>
<cfset arrayAppend(out.instance.availableOperations, this.helper.appendRecord(structNew("linked"), "", local.availableOperationTitleMap, local.qAvailableOperation, this.helper.snake2camel))/>
</cfloop>
<!--- <cfset "out.instance.dependencies0"=#dependencies#/> --->
<cfset "out.instance.dependentInstances"=[]/>
<cfloop query=#local.qDependentInstance#>
<cfset arrayAppend(out.instance.dependentInstances, this.helper.appendRecord(structNew("linked"), "", local.dependentInstanceTitleMap, local.qDependentInstance, this.helper.snake2camel))/>
</cfloop>
<cfset "out.instance.dependencies"=[]/>
<cfloop query=#local.qDependency#>
<cfset arrayAppend(out.instance.dependencies, this.helper.appendRecord(structNew("linked"), "", local.dependencyTitleMap, local.qDependency, this.helper.snake2camel))/>
</cfloop>
<!--- <cfset "out.instance.availableResourceRealms"=[]/>
<cfloop query=#local.qAvailableResourceRealm#>
<cfset arrayAppend(out.instance.availableResourceRealms, this.helper.appendRecord(structNew("linked"), "", local.realmTitleMap, local.qAvailableResourceRealm, this.helper.snake2camel))/>
</cfloop> --->
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<!--- <cfdump var=#local.qCurrentState#/>
<cfdump var=#out#/>
<cfabort/> --->
<cfreturn representationOf(out) />
</cffunction>
<cffunction name="patch">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cfargument name="displayName" type="string" required=false hint="type:string"/>
<!--- <cfargument name="resourceRealmId" type="string" required=false hint="type:integer"/> --->
<cfargument name="descr" type="string" required=false default="" hint="type:varchar (no cleanup here!)"/>
<!--- ****** проверка уникальности --->
<cftry>
<cfset checkDisplayName(arguments.displayName, arguments.usrId, arguments.instanceUid)/><!--- как идея - офорить как обертку, возвращающую то же значение. Но будет соблазн еще оттримить иначе пофиксить, а это уже избыточная функция и должно усложнить название --->
@@ -416,42 +356,28 @@
<cfif structKeyExists(arguments,"displayName")>
,display_name=<cfqueryparam cfsqltype="cf_sql_varchar" value="#htmlEditFormat(arguments.displayName)#" />
</cfif>
<!--- <cfif structKeyExists(arguments,"displayName")>
,resource_realm_id=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.resourceRealmId#" />
</cfif> --->
<cfif structKeyExists(arguments,"descr")>
,descr=<cfqueryparam cfsqltype="cf_sql_varchar" value="#htmlEditFormat(arguments.descr)#" />
</cfif>
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" />;
--select @@rowcount as cnt;
</cfquery>
<!--- <cfif local.qSave.cnt EQ 0>
<cfreturn noData().withStatus(404, "Record Not Found") />
</cfif> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction>
<cffunction name="delete" hint="Удаление экземпляра сервиса по ключу. Допускается в статусе not configured">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cftry>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance e
@@ -465,7 +391,6 @@
"Instance is not accessible for the current user (possibly does not belong to the default specification)")
).withStatus(404)/>
</cfif>
<!--- халява какая-то --->
<cfquery name="local.qCheckOperation" result="local.result">
select count(*) as cnt
@@ -479,7 +404,6 @@
"Operation already started", "Instance deletion disabled when operation is started")
).withStatus(422)/>
</cfif>
<cfquery name="local.qCheckState" result="local.result">
select count(*) as cnt
from instance_state st
@@ -491,7 +415,6 @@
"Instance has a state", "Instance deletion disabled when having state")
).withStatus(422)/>
</cfif>
<!--- мы не включаем проверку доступа в саму операцию удаления, а выполняем отдельно --->
<cfquery name="local.qSave">
delete from instance_state
@@ -501,26 +424,18 @@
delete from instance
where instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid('guid',arguments.instanceUid)#/>;
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- delete --->
<!--- неудобно бегать из одного компонента в соседний при том, что разделение ответственности между ними получается довольно странное
Решил, что логичнее повесить проверки уникальности и генерацию дефолтов на коллекцию, тем более и связей так меньше --->
<cffunction name="checkDisplayName">
<cfargument name="displayName" required=true/>
<cfargument name="usrId" type="numeric" required=true/>
<cfargument name="instanceUid" type="guid" required=true/>
<cfset CreateObject("component", "instance_ls").checkDisplayName(arguments.displayName, arguments.usrId, arguments.instanceUid)/>
</cffunction>
</cfcomponent>
</cfcomponent>
+3 -116
View File
@@ -1,10 +1,8 @@
<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"},
@@ -48,8 +46,6 @@
"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"/>
@@ -57,21 +53,17 @@
<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, будет лаконичнее --->
<!--- *** если идти от 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)))
@@ -81,13 +73,10 @@
</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>
@@ -120,12 +109,10 @@
<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.
@@ -133,7 +120,6 @@
Также на стороне БД приходится конвертировать GUID, потому что драйвер pg jdbc его представляет как структуру из 2 чисел.
Еще jsonb как-то странно сериализуется, в 3 поля, вместо одного Value (решается преобразованием ::text)
--->
<m:field_set titleMapOut="local.titleMap"
lengthOut="local.fieldCount"
nameListOut="local.definedRecordFields"
@@ -154,29 +140,24 @@
<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 --->
@@ -185,8 +166,6 @@
<m:field>st.instance_data->'out'->'monitoring'->>'resourceMetrics' 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'
@@ -198,8 +177,6 @@
) 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 (
@@ -242,10 +219,6 @@
) 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>
@@ -273,23 +246,19 @@
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
@@ -298,7 +267,6 @@
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
@@ -310,52 +278,37 @@
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"/> --->
<!--- оказывается, их можно и не декларировать --->
<cfif !structKeyExists(arguments, "specificationId")>
<cfreturn representationOf("arguments.specificationId not defined").withStatus(500)/>
</cfif>
@@ -365,16 +318,11 @@
<cfif !(arguments.specificationId GT 0)>
<cfreturn representationOf("specification not defined (arguments.specificationId should be a posititve integer)").withStatus(500)/>
</cfif>
<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
@@ -392,7 +340,6 @@
,<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)
@@ -409,28 +356,21 @@
,<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>
@@ -454,20 +394,16 @@
</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
@@ -480,7 +416,6 @@
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">
@@ -491,54 +426,6 @@
</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
--->
+6 -188
View File
@@ -4,25 +4,21 @@
Но: мы не хотели бы менять положение сущности в дереве. Либо она в корне, либо она ниже. И получится, когда мы выбираем один параметр, нам ни к чему контекст - мы к нему однозначно адресуемся.
Проголосуем против избыточности?
--->
<!--- Все методы неявно получают
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>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<!--- cfproperty использовать не надо, это для веб сервисов --->
<cffunction name="init">
<!--- *** ---------------- !!! note camelCase in keys naming!!! ----------------- --->
<!--- для нового инстанса нам нужно знать хотя бы его класс (сервис) --->
@@ -34,7 +30,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset this.instance_operation_cfs_param_uid = "00000000-0000-0000-0000-000000000000"/><!--- *** костыль default=--->
<cfset this.instance_operation_uid = "00000000-0000-0000-0000-000000000000"/><!--- *** костыль default=--->
<cfset this.instance_uid = "00000000-0000-0000-0000-000000000000"/><!--- *** костыль default=--->
<cfif structKeyExists(arguments.keys,"instanceOperationCfsParamUid") AND isValid("guid", arguments.keys.instanceOperationCfsParamUid)>
<cfset this.instance_operation_cfs_param_uid=arguments.keys.instanceOperationCfsParamUid/>
<cfquery name="local.qResolve">
@@ -66,20 +61,15 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfif structKeyExists(arguments.params,"resourceRealm")>
<cfset this.resourceRealm=arguments.params.resourceRealm/>
</cfif>
<!--- <cfdump var=#this# abort=true/> --->
<!--- <cfdump var=#this#/> --->
<cfreturn this>
</cffunction>
<!--- ---------------------------------------------------------------------- --->
<!--- Прикладные функции, которые можно использовать в составе выражений. Аргументами им могут служить параметры, от которых мы зависим --->
<!--- Добавить обработку исключений --->
<cffunction name="dummy" returntype="any" access="public">
<cfargument name="arg" type="any"/>
<cfreturn arg/>
</cffunction>
<cffunction name="concat" returntype="string" access="public">
<cfset var local={}/>
<cfset var s=""/>
@@ -88,7 +78,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfloop>
<cfreturn s/>
</cffunction>
<cffunction name="getValue"><!--- используем пока только для параметров, в принципе может видеть больше, чем положено --->
<cfargument name="token" type="string"/>
<!--- тут такой странный синтаксис: для скоупа params подставляем getCfsParam --->
@@ -103,7 +92,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfif>
<cfreturn variables[scope][name]/><!--- *** вряд ли это отработает для субпараметров --->
</cffunction>
<!--- может быть, обобщить для наследуемых параметров... или для всех вообще --->
<cffunction name="getResourceRealm">
<!--- нужна отдельная функция, потому что resourceRealm может браться из параметров операции, а может наследоваться от родительского инстанса --->
@@ -111,11 +99,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<!--- если операция не сохранена - мы как узнаем, есть у нее параметр resourceRealm, или нет? наверно, простой джойн тут не поможет
но никто не мешает спросить отдельно--->
<!--- *** тут возникает определенная путаница из-за того, что часть данных берется из параметров операции, а часть из параметров инстанса (кумулятивных) --->
<!--- <cfdump var=#this#/> --->
<cfif structKeyExists(this,"resourceRealm") AND len(this.resourceRealm)><!--- может быть, понятнее было бы брать прямо из параметров --->
<cfreturn this.resourceRealm/>
</cfif>
<!--- ищем у даннной операции CFS параметр resourceRealm ---><!--- <cfdump var=#this#/><cfabort/> --->
<!--- ищем у даннной операции CFS параметр resourceRealm --->
<cfquery name="local.qCfsParam">
select p.param_value
from svc_operation_cfs_param s
@@ -123,7 +110,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where s.svc_operation_cfs_param = 'resourceRealm'
AND p.instance_operation_uid = <cfqueryparam cfsqltype="cf_sql_other" value=#this.instance_operation_uid# null=#!isValid('guid',this.instance_operation_uid)#/>
</cfquery>
<!--- <cfdump var=#local.qCfsParam#/> --->
<!--- --->
<cfif local.qCfsParam.recordCount>
<!--- параметр определен в операции сервиса. Если его не нашли - значит, не судьба --->
@@ -136,7 +122,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
и инстанс-операцию-параметр (регулярная реляционная иерархия)
хотя инстанс и операция есть в параметрах (некоторое дублирование, потому что оркестратору все передается через параметры)
--->
<cfset local.instance_uid = this.instance_uid/>
<cfif !isValid("guid", local.instance_uid) OR local.instance_uid EQ "00000000-0000-0000-0000-000000000000">
<!--- тогда нам нужно найти параметр-референс и взять его значение --->
@@ -149,7 +134,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where p.svc_operation_cfs_param_id = <cfqueryparam cfsqltype="cf_sql_integer" value=#this.svc_operation_cfs_param_id# null=#!isValid('integer',this.svc_operation_cfs_param_id)#/>
AND r.ref_svc_id > 0
</cfquery>
<!--- <cfdump var=#local.qRefParam#/> --->
<cfloop query="local.qRefParam">
<cfif structKeyExists(this.params, local.qRefParam.svc_operation_cfs_param) AND isValid('guid', this.params[local.qRefParam.svc_operation_cfs_param])>
<cfset local.instance_uid = this.params[local.qRefParam.svc_operation_cfs_param]/>
@@ -163,7 +147,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
Можно сделать дополнительную фильтрацию по типу справочника (resourceRealm)
--->
</cfif>
<!--- внимание! мы тут опрашиваем параметры инстанса, а не операции! --->
<!--- *** тут возникает непонятная ссылка на себя, впрочем, она не мешает --->
<cfquery name="local.qCfsParam">
@@ -183,9 +166,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
LEFT JOIN instance_cfs_param rp on (e.instance_uid=rp.instance_uid )
LEFT JOIN instance ri on (rp.instance_uid=ri.instance_uid)
WHERE e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value=#local.instance_uid# null=#!isValid('guid',local.instance_uid)#/>
UNION
SELECT
i+1 AS i
,e.display_name
@@ -202,7 +183,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
select param, param_value from r
order by r.i desc
</cfquery>
<cfif local.qCfsParam.recordCount EQ 0>
<!--- тогда зайдем от параметров операции (только в данном экземпляре, зависимости все должны быть уже созданы) --->
<cfquery name="local.qCfsParam">
@@ -223,9 +203,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
--LEFT JOIN instance_cfs_param rp on (e.instance_uid=rp.instance_uid )
--LEFT JOIN instance ri on (rp.instance_uid=ri.instance_uid)
WHERE e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value=#local.instance_uid# null=#!isValid('guid',local.instance_uid)#/>
UNION
SELECT
i+1 AS i
,e.display_name
@@ -243,17 +221,14 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
order by r.i desc
</cfquery>
</cfif>
<!--- <cfdump var=#local.qCfsParam#/>--->
<cfloop query="local.qCfsParam">
<cfif local.qCfsParam.param EQ "resourceRealm">
<cfreturn local.qCfsParam.param_value/>
</cfif>
</cfloop>
<cfreturn "Resource Realm Not Found"/>
</cfif>
</cffunction>
<cffunction name="getCfsParam">
<cfargument name="param" type="string"/>
<cfargument name="subparam" type="string" required=false/>
@@ -264,12 +239,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
при этом мы ожидаем в параметрах структуру paramName:{subparamName:subparamValue}
но почему не находим в сохраненных? возможно такое совпадение, что в сценарии у нас как раз надо выбрать svcId а они не --->
<!--- {"startupConfiguration":{"vdcUid":"ea81db31-2197-407d-b32b-e60577c2671c"}} --->
<!--- this.params <cfdump var=#this.params#/>
arguments <cfdump var=#arguments# /> --->
<cfif structKeyExists(this.params,arguments.param)><!--- AND len(this.params[arguments.param]) --->
<cfif structKeyExists(arguments,"subparam")><!--- *** не изящно, но просто и понятно --->
<cfreturn (this.params[arguments.param])[arguments.subparam]/>
<cfelse>this.params[arguments.param] <!--- <cfdump var=#this.params[arguments.param]#/> --->
<cfelse>this.params[arguments.param]
<cfreturn this.params[arguments.param]/>
</cfif>
</cfif>
@@ -281,7 +254,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where p.instance_operation_uid = <cfqueryparam cfsqltype="cf_sql_other" value=#this.instance_operation_uid# null=#!isValid('guid',this.instance_operation_uid)#/>
AND s.svc_operation_cfs_param = <cfqueryparam cfsqltype="cf_sql_varchar" value=#arguments.param#/>
</cfquery>
<!--- <cfdump var=#this.keys#/> --->
<cfif structKeyExists(arguments,"subparam")><!--- *** не изящно, но просто и понятно --->
<cfset var value = deserializeJson(local.qCfsParam.param_value)/>
<cfif isStruct(value) AND structKeyExists(value, arguments.subparam)>
@@ -293,24 +265,13 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfreturn local.qCfsParam.param_value/>
</cfif>
</cffunction>
<!--- <cffunction name="getValueFromStruct" returntype="string" hint="returns scalar">
<cfargument name="struct" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cftry>
<cfreturn structGet("arguments.struct.#arguments.path#")/>
<cfcatch type="any">
<!--- можно ожидать ошибки, если по указанному пути расположена не структура --->
<cfreturn cfcatch.message/><!--- халява, для упрощения диагностики --->
</cfcatch>
</cftry>
</cffunction> --->
<cffunction name="getValueFromStruct" returntype="any">
<cfargument name="struct" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cftry>
<cfreturn structGet("arguments.struct.#arguments.path#")/>
<cfcatch type="any">
@@ -319,24 +280,18 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcatch>
</cftry>
</cffunction>
<cffunction name="getStringFromStruct" returntype="string" hint="returns scalar">
<cfargument name="struct" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cfset var value = getValueFromStruct(arguments.struct,arguments.path)/>
<cfif isStruct(value) OR isArray(value)>
<cfreturn serializeJson(value)/>
</cfif>
<cfreturn toString(value)/>
</cffunction>
<cffunction name="getKeyListFromStruct" returntype="string" hint="returns list">
<cfargument name="struct" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cftry>
<cfreturn structKeyList(structGet("arguments.struct.#arguments.path#"))/>
<cfcatch type="any">
@@ -346,12 +301,9 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcatch>
</cftry>
</cffunction>
<cffunction name="getArrayAsListFromStruct" returntype="string" hint="returns list">
<cfargument name="struct" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cftry>
<cfreturn arrayToList(structGet("arguments.struct.#arguments.path#"))/>
<cfcatch type="any">
@@ -361,39 +313,11 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcatch>
</cftry>
</cffunction>
<!--- <cffunction name="getInstanceState" returntype="struct">
<cfargument name="instanceUid" type="guid" required="true"/>
<cfset var local={}/>
<cfquery name="local.qInstance" datasource="cmdb">
select g.instance_state_uid, g.version, g.dt_state, g.instance_data, g.is_test, g.instance_uid
from instance_state g
where g.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value=#arguments.instanceUid#/>
order by g.version desc
limit 1
</cfquery>
<cfreturn deserializeJson(local.qInstance.instance_data)/>
<cftry>
<cfset var structConfig = deserializeJson(local.qInstance.instance_data)/>
<cfcatch type="ANY">
<cfreturn {"error":"error parsing state"}/>
</cfcatch>
</cftry>
<cfif len(structConfig)>
<cfreturn structConfig/>
</cfif>
<cfreturn {}/>
</cffunction> --->
<cffunction name="getInstanceState" returntype="struct">
<cfargument name="instanceUid" default=#this.instance_uid#/><!--- type="guid" --->
<cfif !isValid("guid",arguments.instanceUid)>
<cfreturn {}/><!--- иначе при неопределенном параметре мы получаем исключение --->
</cfif>
<cfset var local={}/>
<cfquery name="local.qInstance" datasource="cmdb">
select g.instance_state_uid, g.version, g.dt_state, g.instance_data, g.is_test, g.instance_uid
@@ -402,7 +326,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
order by g.version desc
limit 1
</cfquery>
<!--- <cfreturn deserializeJson(local.qInstance.instance_data)/><cfabort/> --->
<cftry>
<cfset var structConfig = deserializeJson(local.qInstance.instance_data)/>
<cfcatch type="ANY">
@@ -414,19 +337,14 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfif>
<cfreturn structConfig/>
</cffunction>
<cffunction name="getResourceRealmConfig" returntype="struct">
<cfargument name="resourceRealm" type="string" default=#getResourceRealm()#/><!--- платформа может быть определена для родительского инстанса где то в иерархии --->
<cfset var local={}/>
<cfquery name="local.qResourceRealm" datasource="cmdb">
select r.resource_realm_id, r.properties
from resource_realm r
where r.resource_realm=<cfqueryparam cfsqltype="cf_sql_varchar" value=#arguments.resourceRealm#/>
</cfquery>
<!--- <cfdump var=#arguments#/>
<cfdump var=#local.qResourceRealm#/> --->
<cftry>
<cfset var structConfig = deserializeJson(local.qResourceRealm.properties)/>
<cfcatch type="ANY">
@@ -439,17 +357,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfif>
<cfreturn {}/>
</cffunction>
<!--- ReST methods --->
<!--- ----------------------------------------------------------------------------------------- --->
<!--- ----------------------------------------------------------------------------------------- --->
<!--- ----------------------------------------------------------------------------------------- --->
<cffunction name="get" hint="CFS параметр операции инстанса">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cftry>
@@ -458,16 +369,13 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfquery name="local.qCfsParam" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="fieldCount">
<m:field>iop.instance_operation_cfs_param_uid::text as instance_operation_cfs_param_uid</m:field>
<m:field>iop.instance_operation_uid::text as instance_operation_uid</m:field>
<m:field>iop.svc_operation_cfs_param_id</m:field>
<!--- <m:field>iop.param_value</m:field> --->
<m:field>case when sop.is_sensitive then '********' else iop.param_value end as param_value </m:field><!--- внимание! если параметр типа MAP, то значение нужно разобрать и замаскировать секреты --->
<m:field>iop.note</m:field>
<m:field>to_char(iop.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
@@ -482,14 +390,11 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<m:field formatter=#request.castToBool#>sop.is_hidden</m:field>
<m:field formatter=#request.castToBool#>sop.is_disabled</m:field>
<m:field>sop.man</m:field>
<m:field>sop.func</m:field>
<m:field>sop.expression</m:field>
<m:field>sop.nested_ref</m:field>
<m:field>sop.state_path</m:field>
<m:field>sop.depends_on_cfs_params</m:field>
<m:field>sop.config::text as config</m:field>
<m:field>e.service_id</m:field>
<m:field>e.instance_uid::text as instance_uid</m:field>
@@ -503,11 +408,9 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where iop.instance_operation_cfs_param_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationCfsParamUid#" null=#!isValid('guid',arguments.instanceOperationCfsParamUid)#/>
AND si.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
</cfquery>
<cfif local.qCfsParam.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
</cfif>
<cfif len(local.qCfsParam.func)><!--- приоритет отдается функции перед списком значений--->
<cfset var args = {
"functionName":"#local.qCfsParam.func#",
@@ -515,7 +418,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
"contractId":#arguments.contractId#
}/> <!--- если появятся новые аргументы у новых функций, будем добавлять их сюда --->
<cfset local.qCfsParam.value_list=generateValueList(argumentCollection=#args#)/> <!--- можно перечислить именованные аргументы обычным порядком, но с коллекцией потенциально более гибко --->
<!--- <cfdump var=#local.qCfsParam#/><cfabort/> --->
<cfif len(trim(local.qCfsParam.value_list)) AND listLen(local.qCfsParam.value_list) EQ 1>
<cfset local.qCfsParam.default_value=local.qCfsParam.value_list/>
</cfif>
@@ -538,43 +440,32 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cftry>
<cfset local.qCfsParam.value_list = getListFromState(currentState, local.qCfsParam.state_path)/>
</cfif>
<cfif (len(local.qCfsParam.func) OR len(local.qCfsParam.expression) OR len(local.qCfsParam.state_path) OR len(local.qCfsParam.nested_ref))
AND len(local.qCfsParam.value_list) EQ 0>
<cfset local.qCfsParam.value_list = []/>
</cfif>
<cfif local.qCfsParam.data_type EQ "map" OR local.qCfsParam.data_type EQ "map-fixed" OR local.qCfsParam.data_type EQ "array-map-fixed">
<cfset processMap(local.qCfsParam)/><!--- query passed by reference --->
</cfif>
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.instanceOperationCfsParam" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qCfsParam, this.helper.snake2camel
)/>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction><!--- get --->
<cffunction name="delete" hint="Удаление CFS параметра операции по ключу.">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cftry>
<cfset this.helper.validateField(arguments, "instanceOperationCfsParamUid", "guid")/>
<!--- *** Нужна проверка, что операция не была выполнена, иначе она становится архивной --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cftry><!--- *** без проверок существования --->
<cfquery name="local.qCheck" result="local.result">
select
io.dt_submit, io.submit_result
@@ -585,19 +476,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfif left(local.qCheck.submit_result,1) EQ "2"><!--- 201 etc --->
<cfreturn representationOf(this.helper.formatMessage("Operation already started", "Parameter deletion disabled for started operation")).withStatus(422)/>
</cfif>
<!--- <cfquery name="local.qCheckSpecification" result="local.result">
select si.specification_id
from instance_operation_cfs_param iop
join instance_operation io on iop.instance_operation_uid=io.instance_operation_uid
join instance e on (io.instance_uid=e.instance_uid)
left outer join specification_item si on (e.specification_item_id=si.specification_item_id)
where iop.instance_operation_cfs_param_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationCfsParamUid#" null=#!isValid('guid',arguments.instanceOperationCfsParamUid)#/>
AND si.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
</cfquery>
<cfif local.qCheckSpecification.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Instance not accessible", "Instance does not belong to the current specification, contract or contragent")).withStatus(403)/>
</cfif> --->
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance_operation_cfs_param iop
@@ -610,26 +488,20 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfif local.qCheckAccess.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Instance not accessible", "Instance of this CFS parameter is not accessible to the current user (at least does not belong to the default specification)")).withStatus(403)/>
</cfif>
<cfquery name="local.qSave">
delete from instance_operation_cfs_param
where instance_operation_cfs_param_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationCfsParamUid#" null=#!isValid('guid',arguments.instanceOperationCfsParamUid)#/>
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- delete --->
<cffunction name="put" hint="запись CFS параметра операции инстанса">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="paramValue" type="string" required=true/>
<cfargument name="note" type="string" required=false default=""/>
<cfset var local={}/>
<cftry>
<cfset checkInstanceParam(arguments.instanceOperationCfsParamUid, arguments.paramValue, arguments.usrId)/>
@@ -637,7 +509,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfquery name="local.qSave" result="local.result">
update instance_operation_cfs_param set
dt_created=<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Now()#" />
@@ -649,14 +520,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<!--- *** проверить существование - 404 --->
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- put --->
<cffunction name="checkInstanceParam">
<cfargument name="instanceOperationCfsParamUid" type="guid" required=true/>
<cfargument name="paramValue" required=true/>
<cfargument name="usrId" type="numeric" required=true/>
<cfset var local={}/>
<cfquery name="local.qCheck" result="local.result">
select svc_operation_cfs_param_id
@@ -666,11 +533,8 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfif local.qCheck.recordCount EQ 0>
<cfthrow type="invalidParamValue" message="Cannot find CFS parameter by instanceOperationCfsParamUid=#arguments.instanceOperationCfsParamUid#"/>
</cfif>
<cfset CreateObject("component", "instance_operation_cfs_param_ls").checkParam(local.qCheck.svc_operation_cfs_param_id, arguments.paramValue, arguments.usrId, arguments.instanceOperationCfsParamUid)/>
</cffunction>
<cffunction name="processMap" returntype="void" access="public">
<cfargument name="qParam" type="query" required=true/><!--- работаем с текущей строкой query. передача query это скорее колхоз, всего-то надо 4 понятных аргумента.
Конечно, то, что часть из них - id, не упрощает восприяие кода --->
@@ -678,7 +542,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
- сформировать дескриптор
- замаскировать секреты --->
<!--- здесь могут использоваться 2 критерия: формат аргумента, array/struct, либо тип --->
<cfset local.spContainer = {}/><!--- может быть map или array of map --->
<cfset local.spMapArray = []/>
<!--- мы хотим обработать и случай array-map-fixed --->
@@ -693,7 +556,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset local.spMapArray = [{"error":"#cfcatch.message#"}]/><!--- теряем все данные --->
</cfcatch>
</cftry>
<cfquery name="local.qSubparam">
select
sp.svc_operation_cfs_param_id
@@ -722,8 +584,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where sp.svc_operation_cfs_param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.qParam.svc_operation_cfs_param_id#"/>
order by sp.sort
</cfquery>
<!--- <cfdump var=#local.qSubparam#/><cfabort/> --->
<cfquery name="local.qInstSubparam">
select
sp.instance_operation_cfs_param_uid::text as instance_operation_cfs_param_uid
@@ -735,11 +595,9 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where sp.instance_operation_cfs_param_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.qParam.instance_operation_cfs_param_uid#" null=#!isValid("guid",arguments.qParam.instance_operation_cfs_param_uid)#/>
order by sp.sort
</cfquery>
<!--- формируем дескриптор --->
<cfset var mapDescriptor=structNew("linked")/>
<cfset var parser=createObject("component","lib.expression_parser")/>
<cfloop query="local.qSubparam">
<cfset structInsert(mapDescriptor, local.qSubparam.svc_operation_cfs_subparam,
{
@@ -776,12 +634,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
"uniqueScope":"#local.qSubparam.unique_scope#",
"defaultCompute":"#local.qSubparam.default_compute#",
"isModifiableDefinition":false
}, true
)
/>
</cfloop>
<cfloop index="local.i" from=1 to=#arrayLen(local.spMapArray)#><!--- *** искусственное оборачивание в массив вот для этого, не придумал ничего умнее
чтобы не дублировать цикл по субпараметрам. Сделано некрасиво, но работать должно.
Ну а как сделать? Если массив - делать цикл по массиву, а если map-сразу обрабатыввать map. При этом запрос к таблице сабпараметров не хочется делать лишний раз.
@@ -799,7 +655,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfloop>
</cfif>
</cfloop>
<!--- *** проверку, что это не map-fixed, можно добавить здесь --->
<!--- чтобы не вставляли в параметр что попало --->
<!--- здесь мы не делаем цикл по массиву, поскольку не поддерживаем массив расширяемых map --->
@@ -818,21 +673,16 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfif>
</cfloop>
</cfif>
<cfset arguments.qParam.data_descriptor = mapDescriptor/>
<cfif (arguments.qParam.data_type EQ "array-map-fixed")>
<cfset arguments.qParam.param_value = serializeJson(local.spMapArray)/><!--- как есть --->
<cfelse>
<cfset arguments.qParam.param_value = arrayLen(local.spMapArray) ? serializeJson(local.spMapArray[1]) : ""/><!--- вытаскиваем из массива-обертки --->
</cfif>
</cffunction>
<cffunction name="generateValueList" returntype="string" access="public">
<cfargument name="functionName" type="string" required="true"/>
<!--- остальные атрибуты необязательные, в argumentCollection (еще вопрос, насколько безопасно объявлять один из аргументов без остальных) --->
<cfswitch expression=#arguments.functionName#>
<cfcase value="">
<cfreturn ""/>
@@ -848,14 +698,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfdefaultcase>
</cfswitch>
</cffunction>
<cffunction name="getAvailableResourceRealms" returnType="string" access="public">
<cfargument name="svcId"/>
<cfargument name="contractId"/>
<cfset var local={}/>
<cfquery name="local.qAvailableResourceRealm"><!--- *** distinct халява, но придумывать корректную формулировку времени мало --->
select distinct r.resource_realm, r.sort
from resource_realm r
@@ -867,21 +713,16 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
join svc s on r.resource_realm_type_id=s.resource_realm_type_id
where s.svc_id=<cfqueryparam cfsqltype="CF_SQL_INTEGER" value=#arguments.svcId# null=#!isValid("integer", arguments.svcId)#/>
order by r.sort, r.resource_realm
</cfquery> <!--- <cfdump var=#arguments#/> cfdump здесь провоцирует NPE --->
</cfquery>
<cfif local.qAvailableResourceRealm.recordCount>
<cfreturn valueList(local.qAvailableResourceRealm.resource_realm)/>
<cfelse>
<cfreturn ""/><!--- иначе для пустого резалтсета генерируется null --->
</cfif>
</cffunction>
<!--- для вложенных справочников --->
<!--- дублировано: deck-tool/instance_operation.cfm --->
<cffunction name="getSelectedResourceRealm" returnType="string">
<cfset var local={}/>
<cfquery name="local.qSelectedResourceRealm">
select iop.param_value
@@ -890,15 +731,13 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
where iop.instance_operation_uid=<cfqueryparam cfsqltype="CF_SQL_OTHER" value=#d.instance_operation_uid# null=#!isValid("guid", d.instance_operation_uid)#/>
AND sop.svc_operation_cfs_param='resourceRealm'
order by 1
</cfquery> <!--- <cfdump var=#arguments#/> cfdump здесь провоцирует NPE --->
</cfquery>
<cfreturn local.qSelectedResourceRealm.param_value/>
</cffunction>
<cffunction name="generateClosure" returntype="function">
<cfargument name="functionName" type="string" required="true"/>
<cfargument name="config" type="string" required="true"/>
<!--- остальные атрибуты необязательные, в argumentCollection --->
<cfswitch expression=#arguments.functionName#>
<cfcase value="">
<cfreturn function(){}/>
@@ -916,19 +755,15 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfdefaultcase>
</cfswitch>
</cffunction>
<cffunction name="getRefFromRRealmConfig" returntype="any">
<cfargument name="resourceRealm" type="string" required="true"/>
<cfargument name="config" type="string" required="true"/>
<cfquery name="qResourceRealmConfig">
select properties::text
from resource_realm
where resource_realm='ngcloud.ru'
<!--- <cfqueryparam cfsqltype="cf_sql_varchar" value=#arguments.resourceRealm#/> --->
</cfquery>
<!--- <cfset var local={}/> ---> <!--- паранойя... и есть шансы, что я неправильно понимаю эту химию скоупов--->
<!--- паранойя... и есть шансы, что я неправильно понимаю эту химию скоупов--->
<cfset var props = deserializeJson(qResourceRealmConfig.properties)/>
<cfset var cfg = deserializeJson(arguments.config)/> <!--- удобнее было бы передавать сразу структуру впр --->
<cfset cfg={"path":"vcd.providerVdc"}/><!--- *** временный пример для отладки --->
@@ -940,12 +775,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
<cfset structInsert(result, containerName, structGet("props.#path#"))/>
<cfreturn result/>
</cffunction>
<!--- duplicated in deck-tool --->
<cffunction name="getListFromState" returntype="string" hint="returns list">
<cfargument name="state" type="struct" required="true"/>
<cfargument name="path" type="string" required="true"/>
<cftry>
<cfreturn structKeyList(structGet("arguments.state.#arguments.path#"))/>
<cfcatch type="any">
@@ -954,20 +787,5 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>
<!--- [{"b":"12","secret1":"preved"},{"b":"13","secret1":"preved404"},{"b":"14","secret1":"preved402"}] --->
<!--- [{"b":"12","secret1":"preved"},{"b":"13","secret1":"preved404"},{"b":"14","secret1":"preved402"}] --->
@@ -1,12 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperationCfsParams/{instanceOperationCfsParamUid}/subparams/{subparam}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<cffunction name="get" hint="Метаданные субпараметра CFS-параметра операции экземпляра (значение субпараметра находится в теле параметра)"><!--- *** спросить, может, метод не нужен, а если нужен - наверно, нужно значение --->
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="subparam" type="string" required=true hint="type:string"/>
@@ -16,9 +12,7 @@
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfquery name="local.qRead" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="fieldCount">
@@ -26,7 +20,6 @@
<m:field>ios.instance_operation_cfs_subparam</m:field>
<m:field formatter=#request.castToBool#>ios.is_sensitive</m:field>
<m:field>ios.note</m:field>
<!--- <m:field>iop.data_type</m:field> --->
<m:field>to_char(ios.dt_created, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_created</m:field>
</m:field_set>
from instance_operation_cfs_subparam ios
@@ -38,45 +31,31 @@
AND ios.instance_operation_cfs_subparam=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.subparam#"/>
AND si.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/> /*tenant isolation, из конфиденциального тут заметки... может быть, имена переменных окружения*/
</cfquery>
<cfif local.qRead.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
</cfif>
<!--- <cfif local.qRead.data_type NEQ 'map'>
<cfreturn representationOf(this.helper.formatMessage("Parent parameter type does not have subparameters")).withStatus(400)/>
</cfif> --->
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.instanceOperationCfsSubparam" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qRead, this.helper.snake2camel
)/>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction><!---/get --->
<cffunction name="delete" hint="Удаление метаданных субпараметра CFS-параметра операции экземпляра по ключу.">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="subparam" type="string" required=true hint="type:string"/>
<cftry>
<cfset this.helper.validateField(arguments, "instanceOperationCfsParamUid", "guid")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cftry>
<!--- проверка существования одновременно может служить для изоляции тенантов --->
<!--- без проверки стартовавшей операции --->
<!--- нужна проверка на изоляцию тенантов --->
<cfquery name="local.qCheckExistence" result="local.result">
select count(*) as cnt
from instance_operation_cfs_subparam ios
@@ -86,7 +65,6 @@
<cfif local.qCheckExistence.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found", "Subparameter specified does not exist")).withStatus(404)/>
</cfif>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance_operation_cfs_param iop
@@ -99,40 +77,31 @@
<cfif local.qCheckAccess.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Accessible", "CFS parameter specified is not accessible by the current user")).withStatus(403)/>
</cfif>
<cfquery name="local.qSave">
delete from instance_operation_cfs_subparam
where instance_operation_cfs_param_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceOperationCfsParamUid#" null=#!isValid('guid',arguments.instanceOperationCfsParamUid)#/>
AND instance_operation_cfs_subparam=<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.subparam#"/>
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- *** это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- delete --->
<cffunction name="put" hint="запись метаданных субпараметра CFS-параметра операции инстанса">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="subparam" type="string" required=true hint="type:string"/>
<cfargument name="isSensitive" type="boolean" required=false default=false hint="type:boolean"/>
<cfargument name="note" type="string" required=false default=""/> <!--- *** XSS!!! --->
<cftry>
<cfset this.helper.validateField(arguments, "instanceOperationCfsParamUid", "guid")/>
<cfset this.helper.validateField(arguments, "isSensitive", "boolean")/>
<!--- *** Нужна проверка, что операция не была выполнена, иначе она становится архивной --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset var local={}/>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance_operation_cfs_param iop
@@ -145,7 +114,6 @@
<cfif local.qCheckAccess.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Accessible", "Instance CFS parameter not found or access denied")).withStatus(404)/>
</cfif>
<cfquery name="local.qCheckExistence" result="local.result">
select count(*) as cnt
from instance_operation_cfs_subparam iop
@@ -155,7 +123,6 @@
<cfif local.qCheckExistence.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found", "Subparameter specified does not exist")).withStatus(404)/>
</cfif>
<cfquery name="local.qSave" result="local.result">
update instance_operation_cfs_subparam set
dt_updated=<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Now()#" />
@@ -168,5 +135,4 @@
<!--- *** проверить существование - 404 --->
<cfreturn noData().withStatus(204, "No Content") />
</cffunction><!--- put --->
</cfcomponent>
</cfcomponent>
@@ -1,25 +1,15 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperationCfsParams/{instanceOperationCfsParamUid}/subparams">
<!--- забавно, что мы организуем эндпойнт коллекции только ради операции создания. Список (коллекция) отдельно не используется, а выгружается в составе более высокоуровневых сущностей --->
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<cffunction name="post" hint="создание метаданных субпараметра CFS параметра операции инстанса">
<cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="subparam" type="string" required=true hint="type:string"/>
<cfargument name="isSensitive" type="boolean" required=false default=false hint="type:boolean"/>
<cfargument name="note" type="string" required=false default="" hint="type:string"/>
<!--- arguments.specificationId injected implicitly --->
<!--- <cfargument name="instanceOperationCfsParamUid" type="string" required=true hint="type:guid"/>
<cfargument name="subparam" type="string" required=true hint="type:string"/>
<cfargument name="isSensitive" type="boolean" required=true/>
<cfargument name="note" type="string" required=false default=""/> --->
<cftry>
<cfset this.helper.validateField(arguments, "instanceOperationCfsParamUid", "guid")/>
<cfset this.helper.validateField(arguments, "isSensitive", "boolean")/>
@@ -27,9 +17,7 @@
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance_operation_cfs_param iop
@@ -42,7 +30,6 @@
<cfif local.qCheckAccess.cnt EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Instance CFS parameter not found or access denied")).withStatus(404)/>
</cfif>
<cfquery name="local.qCheckDuplicates" result="local.result">
select count(*) as cnt
from instance_operation_cfs_subparam ios
@@ -52,7 +39,6 @@
<cfif local.qCheckDuplicates.cnt GT 0>
<cfreturn representationOf(this.helper.formatMessage("Subparameter already exists")).withStatus(400)/>
</cfif>
<cfquery name="local.qSave" result="local.result">
insert into instance_operation_cfs_subparam
(instance_operation_cfs_param_uid, instance_operation_cfs_subparam, is_sensitive, note, dt_created, creator_id, dt_updated, updater_id)
@@ -68,14 +54,8 @@
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usrId#" />
);
</cfquery>
<cfreturn noData()
.withStatus(201, "Created")
/>
</cffunction><!--- get --->
</cfcomponent>
</cfcomponent>
@@ -1,28 +1,21 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperations/{instanceUid}/{svcOperationId}" hint="template for default instance operation. Legacy URL: /instanceOperations/default/{svcOperationId}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<cffunction name="get" hint="Шаблон операции экземпляра">
<cfargument name="instanceUid" type="string" required=true hint="type:guid|'default'"/>
<cfargument name="svcOperationId" type="string" required=true hint="type:integer"/>
<cftry>
<cfif arguments.instanceUid NEQ 'default'>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
</cfif>
<cfset this.helper.validateField(arguments, "svcOperationId", "integer")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset var local={}/>
<cfquery name="local.qSvcOperation" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
@@ -36,11 +29,9 @@
from svc_operation o
where o.svc_operation_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.svcOperationId#" null=#!isValid('integer',arguments.svcOperationId)#/>
</cfquery>
<cfif local.qSvcOperation.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
</cfif>
<!--- получаем параметры из свежего стейта --->
<cfset local.instanceDataParams = {}/>
<cfset var currentState = {}/>
@@ -62,13 +53,10 @@
</cfcatch>
</cftry>
</cfif>
<cfquery name="local.qCfsParam" result="local.result">
select
<m:field_set titleMapOut="local.cfsParamMap" lengthOut="local.fieldCount">
<m:field title="ID" cfSqlType="CF_SQL_INTEGER">p.svc_operation_cfs_param_id</m:field>
<!--- <m:field cfSqlType="CF_SQL_INTEGER">p.svc_operation_id</m:field> --->
<m:field>p.svc_operation_cfs_param</m:field>
<m:field>p.label</m:field>
<m:field>p.data_type</m:field>
@@ -92,7 +80,6 @@
<m:field>p.man</m:field>
<m:field>p.sort</m:field>
<m:field>p.nested_ref</m:field>
<!--- <m:field>p.config::text as config</m:field> --->
<m:field>p.config::text</m:field><!--- в таком виде конфиг доступен для кода, но не выводится в респонсе --->
<m:field>null as nested_ref_data</m:field>
<m:field>p.state_path</m:field>
@@ -114,8 +101,6 @@
AND p.is_actual
order by p.sort, p.svc_operation_cfs_param_id
</cfquery>
<cfloop query="local.qCfsParam">
<cfif len(local.qCfsParam.func)><!--- приоритет отдается функции перед списком значений--->
<cfset var args = {
@@ -124,7 +109,6 @@
"contractId":#arguments.contractId#
}/> <!--- если появятся новые аргументы у новых функций, будем добавлять их сюда --->
<cfinvoke component="instance_operation_cfs_param" method="generateValueList" argumentCollection=#args# returnVariable="local.qCfsParam.value_list"/>
<cfif len(trim(local.qCfsParam.value_list)) AND listLen(local.qCfsParam.value_list) EQ 1>
<cfset local.qCfsParam.default_value=local.qCfsParam.value_list/><!--- *** не очень красиво перетирать query, но делаем это довольно часто --->
</cfif>
@@ -147,12 +131,10 @@
returnVariable="local.qCfsParam.value_list"
/>
</cfif>
<cfif (len(local.qCfsParam.func) OR len(local.qCfsParam.expression) OR len(local.qCfsParam.state_path) OR len(local.qCfsParam.nested_ref))
AND len(local.qCfsParam.value_list) EQ 0>
<cfset local.qCfsParam.value_list = []/>
</cfif>
<cfif data_type EQ "map" OR data_type EQ "map-fixed" OR data_type EQ "array-map-fixed"><!--- *** частичное дублирование с instance_operation_cfs_param.processMap --->
<!--- без полей инстанса, только шаблон --->
<cfquery name="local.qSubparam">
@@ -184,43 +166,31 @@
where sp.svc_operation_cfs_param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#svc_operation_cfs_param_id#"/>
order by sp.sort
</cfquery>
<cfset var mapDescriptor=structNew("linked")/>
<!--- *** выдавать на фронт expression нам ни к чему, это временное явление --->
<cfloop query="local.qSubparam">
<cfif len(local.qSubparam.func)><!--- приоритет отдается функции перед списком значений--->
<cfset var args = {
"functionName":"#local.qSubparam.func#",
"svcId":#local.qSvcOperation.svc_id#,
"contractId":#arguments.contractId#
}/>
<cfinvoke component="instance_operation_cfs_param" method="generateValueList" argumentCollection=#args# returnVariable="local.qSubparam.value_list"/>
<cfif len(trim(local.qSubparam.value_list)) AND listLen(local.qSubparam.value_list) EQ 1>
<cfset local.qSubparam.default_value=local.qSubparam.value_list/><!--- *** не очень красиво перетирать query, но делаем это довольно часто --->
</cfif>
</cfif>
<cfif (len(local.qSubparam.func) OR len(local.qSubparam.expression) )
AND len(local.qSubparam.value_list) EQ 0>
<cfset local.qSubparam.value_list = []/>
</cfif>
<cfset var def = computeDefault(local.qSubparam.default_compute, local.qSubparam.default_value, qSvcOperation.svc_id, arguments.usrId)/>
<!--- *** пока не пытаемся восстановить значения из стейта
<cfif len(local.qSubparam.default_value) EQ 0>
<!--- и вот наконец мы извлекаем старое значение из стейта --->
<cfset var paramName = this.helper.snake2camel(local.qSubparam.svc_operation_cfs_subparam)/>
<cfif structKeyExists(local.instanceDataParams, paramName)>
<cfset var oldValue = structFind(local.instanceDataParams, paramName)/>
<cfset def = isStruct(oldValue) ? serializeJson(oldValue) : oldValue/> <!--- потому что передаем строку, а не объект --->
</cfif>
</cfif> --->
<cfset structInsert(mapDescriptor, local.qSubparam.svc_operation_cfs_subparam,
{
"svcOperationCfsSubparamId":"#local.qSubparam.svc_operation_cfs_subparam_id#",
@@ -248,27 +218,17 @@
}, true
)
/><!--- *** вообще-то передавать в дескриптор func и default_compute не нужно, это для не забыть --->
</cfloop>
<cfset local.qCfsParam.data_descriptor = mapDescriptor/>
</cfif><!---/map --->
</cfloop>
<!--- <cfdump var=#local.qCfsParam# abort=true/> --->
<!--- <cfset queryDeleteColumn(local.qCfsParam, "func")/> --->
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.svcOperation" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qSvcOperation, this.helper.snake2camel
)/>
<cfset "out.svcOperation.cfsParams"=[]/>
<!--- подставляем значение из стейта, если оно там есть (некорректно, потому что в стейте RFS параметры) --->
<cfloop query=#local.qCfsParam#>
<cfset local.qCfsParam.default_value = computeDefault(local.qCfsParam.default_compute, local.qCfsParam.default_value, qSvcOperation.svc_id, arguments.usrId)/><!--- *** не очень хорошо модифицировать запрос --->
<cfif len(local.qCfsParam.default_value) EQ 0>
@@ -280,16 +240,11 @@
</cfif>
</cfif>
<cfset var rec = this.helper.appendRecord(structNew("linked"), "", local.cfsParamMap, local.qCfsParam, this.helper.snake2camel)/>
<!--- <cfdump var=#rec# abort=true/> --->
<cfset arrayAppend(out.svcOperation.cfsParams, rec)/>
</cfloop>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction>
<!--- *** выглядит ужасно, но красивый вариант не придумывается ряд месяцев --->
<!--- *** Внимание! нужно учитывать скоуп уникальности --->
<cffunction name="computeDefault">
@@ -297,7 +252,6 @@
<cfargument name="original"/>
<cfargument name="service_id" type="numeric"/>
<cfargument name="usr_id" type="numeric"/>
<cfswitch expression=#arguments.defaultCompute#>
<cfcase value="display_name">
<cfset var dnGenerator = CreateObject("component", "instance_ls")/>
@@ -316,7 +270,5 @@
<cfreturn #arguments.original#/>
</cfdefaultcase>
</cfswitch>
</cffunction>
</cfcomponent>
+2 -51
View File
@@ -1,10 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperations">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<!---спецификация полей, пригодных для фильтрации (и сортировки)--->
<cfset this.fieldsSpec = {
"instance_operation_uid"={prefix="o", type="guid"},
@@ -31,27 +29,18 @@
"note"={prefix="o", type="string"},
"duration"={expression="extract('epoch' from coalesce(o.dt_finish,CURRENT_TIMESTAMP)- o.dt_start)", type="numeric"}
}/>
<!--- <cffunction name="post" hint="Создание новой операции с параметрами. Принимаются CFS параметры, генерируются RFS параметры. Запускается операция отдельным вызовом. До запуска CFS параметры могут корректироваться (а надо?). Запущенная операция навсегда становится Read Only (при желании можем реализовать ее клонирование). Можем реализовать корректировку параметров, но непонятно, зачем. Когда мы конфигурируем инстанс, на самом деле мы задаем CFS параметры операции create. Можно править параметры, а можно пересоздать операцию с новыми параметрами. Предусмотреть удаление (метод DELETE) незапущенной операции?"> --->
<cffunction name="get" hint="Список операций">
<cfargument name="pageSize" type="string" hint="type:integer" default="100"/>
<cfargument name="page" type="string" hint="type:integer" default="1"/>
<cfargument name="orderBy" type="string" hint="type:string, description:comma-separated list of fields to sort by, example:fld1.ASC,fld2.DESC,fld3.ASC" default=""/>
<cfset var local={}/>
<cftry>
<!---разбор и проверка параметров запроса--->
<cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
@@ -59,8 +48,6 @@
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
<cfquery name="local.qRead" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
@@ -112,7 +99,6 @@
offset #arguments.pageSize*(arguments.page-1)#
</cfif>
</cfquery>
<cfquery name="local.qCnt">
select count(*) as cnt
#local.fromClause#
@@ -120,60 +106,43 @@
s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
<m:filter_build filter=#filter#/>
</cfquery>
<cfquery name="local.qTotal">
select count(*) as cnt
#local.fromClause#
<!--- from instance_operation o
join instance e on (o.instance_uid=e.instance_uid)
left outer join specification_item i on (e.specification_item_id=i.specification_item_id)
left outer join specification s on (i.specification_id=s.specification_id) --->
where
s.specification_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#arguments.specificationId#/>
</cfquery>
<cfset 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#>
<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/>
<cfreturn representationOf(out)/>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
</cffunction>
<cffunction name="post" hint="Создание новой операции. Отдельно загружаются CFS параметры, генерируются RFS параметры. Запускается операция отдельным вызовом. До запуска CFS параметры могут корректироваться (а надо?). Запущенная операция вместе с параметрами навсегда становится Read Only (при желании можем реализовать ее клонирование). Можем реализовать корректировку параметров, но непонятно, зачем. Когда мы конфигурируем инстанс, на самом деле мы задаем CFS параметры операции create. Можно править параметры, а можно пересоздать операцию с новыми параметрами. Предусмотреть удаление (метод DELETE) незапущенной операции?">
<cfargument name="instanceUid" type="string" required=true hint="type:guid"/>
<cfargument name="operation" type="string" required=true hint="type:string decsiption: operation name, e.g. create"/>
<cfargument name="note" type="string" required=false default="" hint="type:string decsiption: user notes"/>
<!--- <cfargument name="resourceRealmId" type="string" default=-1 hint="type:integer decsiption: идентификатор ресурсной платформы"/> ---><!--- *** --->
<!--- *** --->
<!--- *** полноценная валидация операции предполагает проверку, что операция поддерживается для данного сервиса и текущего состояния инстанса. Можно представить себе зависимость этого от параметров, но хотелось бы избежать такого усложнения --->
<!--- Можно отметить, что, например, допустимость операции delete зависит от контекста - сколько времени сервис остановлен --->
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfquery name="local.qCheckOperation" result="local.result">
select count(*) as cnt
from svc_operation so
@@ -185,7 +154,6 @@
<cfif local.qCheckOperation.cnt EQ 0>
<cfthrow type="invalidParamValue" message="Operation unsupported for this service/instance" detail="#arguments.operation#"/>
</cfif>
<cfquery name="local.qCheckAccess" result="local.result">
select count(*) as cnt
from instance e
@@ -199,19 +167,12 @@
</cfif>
<!--- *** Добавить проверку разрешенных переходов, например, create нельзя сделать на развернутом экземпляре
*** Вероятно, допустимые переходы нужно либо специфицировать в документации, либо явно опубликовать (инстанс может публиковать список допустимых операций) --->
<!--- <cfif listFind("create,redeploy",arguments.operation)>
<cfset checkResourceRealmId(arguments.resourceRealmId, arguments.instanceUid)/>
</cfif> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset local={}/>
<cfset local.instanceOperationUid=#createGUID()#/>
<cftry>
<cfquery name="local.qSave">
insert into instance_operation
@@ -228,30 +189,24 @@
,<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usrId#" />
);
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(this.helper.formatException(cfcatch, "Internal Error")).withStatus(500)/><!--- это не нужно показывать в продуктиве --->
</cfcatch>
</cftry>
<cfreturn noData()
.withHeaders({location: "./#local.instanceOperationUid#"})
.withStatus(201, "Created")
/>
</cffunction>
<!--- *** NOT USED --->
<cffunction name="checkResourceRealmId" returntype="void" hint="deprecated">
<!--- В данном случае мы проверяем соответствие платформы сервису, а не операции, поэтому вопрос, нужен ли для данной операции параметр resourceRealm, надо решать вне этой функции --->
<cfargument name="resourceRealmId" type="numeric" required=true/>
<cfargument name="instanceUid" type="guid" required=true/>
<cfset var local={}/>
<cfif NOT resourceRealmId GT 0>
<cfthrow type="InvalidParamValue" message="Resource realm undefined" detail="Не выбрана ресурсная платформа"/><!--- *** для многих операций она и не нужна --->
</cfif>
<cfquery name="local.qContract">
select c.contract_id
from instance e
@@ -260,7 +215,6 @@
join contract c on (s.contract_id=c.contract_id)
where e.instance_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.instanceUid#" null=#!isValid("guid",arguments.instanceUid)#/>
</cfquery>
<cfquery name="local.qCheckResourceRealmType">
select r.resource_realm_type_id
from resource_realm r
@@ -273,7 +227,6 @@
<cfif local.qCheckResourceRealmType.recordCount EQ 0>
<cfthrow type="InvalidParamValue" message="Resource realm does not match service" detail="Ресурсная платформа не соответствует сервису"/>
</cfif>
<cfquery name="local.qCheckResourceRealmAccess">
select r.resource_realm_id
from resource_realm r
@@ -283,7 +236,6 @@
OR a.contract_id=0)
AND a.is_enabled
</cfquery>
<cfif local.qCheckResourceRealmAccess.recordCount EQ 0>
<cfthrow type="InvalidParamValue" message="Access to the resource realm denied for current contract" detail="У текущего договора id=#local.qContract.contract_id# нет доступа к выбранной ресурсной платформе"/>
</cfif>
@@ -297,5 +249,4 @@
--->
<cfreturn/>
</cffunction>
</cfcomponent>
</cfcomponent>
@@ -1,24 +1,15 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instanceOperations/{instanceOperationUid}/validate-cfs">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<cffunction name="get" hint="Проверка корректности CFS">
<cfargument name="instanceOperationUid" type="string" required=true hint="type:guid"/>
<!--- <cfset var local={}/> --->
<!--- *** мы не проверяем доступ к инстансу и существование инстанса 2DO --->
<cfset var validationMessage = createObject("component","instance_operation").validateCfsParams(arguments.instanceOperationUid)/>
<cfif len(validationMessage)>
<cfreturn representationOf(this.helper.formatMessage("Inconsistent CFS parameters", #validationMessage#)).withStatus(422)/>
</cfif>
<cfreturn noData().withStatus(204, "OK") />
</cffunction>
</cfcomponent>
+1 -19
View File
@@ -1,11 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/services/{svcId}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<cffunction name="get" hint="Сервис"><!--- *** TODO проверка принадлежности тенанту --->
<cfargument name="svcId" type="string" required=true hint="type:integer"/>
<cftry>
@@ -14,10 +11,6 @@
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<!--- <cfdump var=#arguments#/> <cfabort/> --->
<cfquery name="local.qSvc" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
@@ -37,7 +30,6 @@
from svc s
where svc_id=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.svcId#" null=#!isValid('integer',arguments.svcId)#/>
</cfquery>
<cfquery name="local.qSvcOperation" result="local.result">
select
<m:field_set titleMapOut="local.operationsMap" lengthOut="local.fieldCount">
@@ -50,29 +42,19 @@
from svc_operation j
where j.svc_id=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.svcId#" null=#!isValid('integer',arguments.svcId)#/>
</cfquery>
<cfif local.qSvc.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
</cfif>
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<!--- <cfset var rec={}/>
<cfset this.helper.appendRecord(rec, "", local.titleMap, local.qSvc, this.helper.snake2camel)/>
<cfset "out.svc"=rec/> --->
<cfset "out.svc" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qSvc, this.helper.snake2camel
)/>
<cfset "out.svc.operations"=[]/>
<cfloop query=#local.qSvcOperation#>
<cfset arrayAppend(out.svc.operations, this.helper.appendRecord(structNew("linked"), "", local.operationsMap, local.qSvcOperation, this.helper.snake2camel))/>
</cfloop>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction>
</cfcomponent>
</cfcomponent>
+4 -53
View File
@@ -1,12 +1,9 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/servicesGrouped">
<!--- Внимание! Дублирование семантики с svc_ls (как правило, вносить изменения параллельно --->
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<cfset this.fieldsSpec={
dt_created={prefix="s", type="date"},
@@ -28,41 +25,26 @@
resource_realm_cnt={type="integer"}
}
/>
<cffunction name="get" hint="Список сервисов с группами для каталога. Без управления страницами и сортировкой (сортировка по порядку группы, сервисы вне групп в конце списка). Если сервис входит в несколько групп, он будет повторяться">
<!--- <cfargument name="pageSize" type="string" hint="type:integer" default=500/>
<cfargument name="page" type="string" hint="type:integer" default="1"/>
<cfargument name="orderBy" type="string" hint="type:string, description:comma-separated list of fields to sort by, example:fld1.ASC,fld2.DESC,fld3.ASC" default=""/> --->
<cfset var local={}/>
<!---parse and validate request parameters--->
<cftry>
<!--- <cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/> --->
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<!--- <cfset var order=this.helper.parseOrderBy(this.fieldsSpec, arguments.orderBy)/> --->
<cfcatch type="invalidParamValue"><!--- <cfreturn representationOf("Проверка связи 3")> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
<cfset var out=structNew("linked")/>
<!--- <cfset var maxrows=arguments.pageSize*arguments.page/>
<cfset var startrow=arguments.pageSize*(arguments.page-1)+1/> --->
<cfquery name="local.qRead" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
<m:field title="ID" cfSqlType="CF_SQL_INTEGER">s.svc_id</m:field>
<m:field title="Сервис">s.svc</m:field>
<m:field title="Сокращение">s.svc_short</m:field>
<!--- <m:field>s.orchestrator_name</m:field> --->
<m:field title="Код">s.code</m:field>
<m:field title="URL создания">(select url from svc_operation so where so.operation='create' AND so.svc_id=s.svc_id limit 1) as create_url</m:field>
<m:field formatter=#function(x){return x NEQ 0}#>s.is_production_ready</m:field>
@@ -98,72 +80,41 @@
)
where 1=1 <m:filter_build filter=#filter#/>
order by g.sort, g.svc_group_id
<!--- <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=0/><!---no sort --->length limit--->
length limit--->
<!--- limit #maxrows# --->
</cfquery>
<!--- <cftry>
<cfquery name="local.qTotal">
select count(*) as cnt
from svc
where 1=1
</cfquery>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry> --->
<!--- <cfdump var=#qRead#/><cfabort/> --->
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<!--- <cfset "out.total"=#local.qTotal.cnt#/> --->
<cfset "out.resultSetSize"=#local.qRead.recordCount#/>
<!--- <cfset "out.pageSize"=#arguments.pageSize*1#/>
<cfset "out.page"=#arguments.page*1#/>
<cfset "out.orderBy"=#arguments.orderBy#/> --->
<cfset var resultCollection=[]/>
<cfset var svcMap = structCopy(local.titleMap)/><!--- ради аккуратности, дальше local.titleMap все равно не используем --->
<cfset var groupMap = withdrawStructKeys(svcMap, "svc_group_id,svc_group")/>
<cfloop query=#local.qRead# group="svc_group_id"<!--- startRow=#startrow# endRow=#(startrow+maxrows-1)# --->>
<cfset var grp = structNew("linked")/>
<cfset this.helper.appendRecord(grp, "", groupMap, local.qRead, this.helper.snake2camel)/>
<cfset "grp.services"=[]/>
<cfset var svcRec = structNew("linked")/>
<cfloop>
<cfset this.helper.appendRecord(svcRec, "", svcMap, local.qRead, this.helper.snake2camel)/>
<cfset arrayAppend(grp.services, structCopy(svcRec))/>
<!--- <cfdump var=#svcRec#/> --->
</cfloop>
<cfset arrayAppend(resultCollection, structCopy(grp))/>почему-то здесь работает без structCopy
</cfloop>
<cfset "out.size"=#arrayLen(resultCollection)#/>
<cfset "out.results"=#resultCollection#/>
<cfset "out.runDurationMs"=getTickCount()-request.startTickCount/> <!--- <cfabort/> --->
<!---<cfset "out.sql"=#local.result.sql#/>--->
<cfset "out.runDurationMs"=getTickCount()-request.startTickCount/>
<cfreturn representationOf(out)/>
</cffunction>
<cffunction name="withdrawStructKeys" access="private" returntype="struct">
<!--- переносим ключи из входной структуры в возвращаемую (во входной удаляем) --->
<cfargument name="structFrom" type="struct"/>
<cfargument name="keyList" type="string"/>
<cfset var structOut = {}/>
<cfloop list=#arguments.keyList# item="key">
<cfif structKeyExists(arguments.structFrom, key)>
<cfset structInsert(structOut, key, structFind(structFrom, key))/>
<cfset structDelete(arguments.structFrom, key)/>
</cfif>
</cfloop>
<cfreturn structOut/>
</cffunction>
</cfcomponent>
</cfcomponent>
+2 -40
View File
@@ -1,10 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/services">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<!---спецификация полей, пригодных для фильтрации (?и сортировки)--->
<!---здесь для простоты без префикса, потому что мы используем вложенный селект, чтобы сделать композитное поле--->
<cfset this.fieldsSpec={
@@ -27,7 +25,6 @@
"resource_realm_cnt"={prefix="sv", type="integer"}
}
/>
<!--- *** пока сортировка, пагинация и состав полей компонуются так, что приходится добавлять поле сортировки в резалтсет --->
<cffunction name="get" hint="Список сервисов">
<cfargument name="pageSize" type="string" hint="type:integer" default=100/>
@@ -35,24 +32,17 @@
<cfargument name="orderBy" type="string" hint="type:string, description:comma-separated list of fields to sort by, example:fld1.ASC,fld2.DESC,fld3.ASC" default=""/>
<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=""/>
<cfset local={}/>
<!--- Фильтрация не униицирована (разница с instance_ls) --->
<!---parse and validate request parameters--->
<cftry>
<cfset this.helper.validateField(arguments, "pageSize", "integer")/>
<cfset this.helper.validateField(arguments, "page", "integer")/>
<!---мы мирно игнорируем поля, отсутствующие в спецификации, что позволяет не делать исключения для orderBy и т.п.--->
<cfset var filter=this.helper.parseFilterParams(this.fieldsSpec)/>
<cfset var order=this.helper.parseOrderBy(this.fieldsSpec, arguments.orderBy)/>
<cfset local.fieldsToOutput=""/>
<cfloop collection=#this.fieldsSpec# item="fld">
<!--- <cfoutput>#this.helper.snake2camel(trim(fld))#</cfoutput> --->
<!--- *** в данном случае мы некорректно ограничиваем состав выводимых полей
полями, перечисленными в fieldSpec --->
<cfif listFindNoCase(arguments.fields, this.helper.snake2camel(trim(fld)))
@@ -60,13 +50,10 @@
<cfset local.fieldsToOutput=listAppend(local.fieldsToOutput,trim(fld))/>
</cfif>
</cfloop>
<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>
@@ -81,8 +68,6 @@
<cfset local.fieldsShortList=listAppend(local.fieldsShortList,fld)/>
</cfif>
</cfloop>
<!--- *** Не нужно ли сохранять префикс? (на случай одноименных полей) --->
<cfloop collection=#order# item="item">
<cfset var ord = structFind(order,item)/>
@@ -101,21 +86,16 @@
<cfset local.fieldsShortList=listAppend(local.fieldsShortList,fld)/>
</cfif>
</cfloop>
<cfcatch type="invalidParamValue"><!--- <cfreturn representationOf("Проверка связи 3")> --->
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
<cfset var out=structNew("linked")/>
<cfset var maxrows=arguments.pageSize*arguments.page/>
<cfset var startrow=arguments.pageSize*(arguments.page-1)+1/>
<m:field_set
titleMapOut="local.titleMap"
lengthOut="local.fieldCount"
@@ -152,14 +132,6 @@
<m:field>to_char(s.dt_updated, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_updated</m:field>
<m:field>(coalesce(s.svc,'') ||' '|| coalesce(s.svc_short,'') ||' '|| coalesce(s.synonyms,'')) as svc_extended_name</m:field>
</m:field_set>
<!--- <cfdump var=#local.titleMap#/>
<cfdump var=#arguments.orderBy#/>
<cfdump var=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)#/>
<cfdump var=#local.fieldsToOutput#/>
<m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=1/>
<cfabort/> --->
<!--- *** проблема: сортировка строится некорректно. пока добавим поля сортировки в резалтсет --->
<cfquery name="local.qRead" result="local.result">
select <cfif listLen(local.definedRecordFields)>#local.definedRecordFields#<cfelse>*</cfif> from
@@ -184,8 +156,6 @@
where 1=1 <m:filter_build filter=#filter#/>
order by <m:order_build sortCollection=#this.helper.parseNumericOrder(local.titleMap, arguments.orderBy)# fieldCount=1/>
</cfquery>
<cftry>
<cfquery name="local.qTotal">
select count(*) as cnt
@@ -196,29 +166,21 @@
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
<!--- <cfreturn representationOf("Проверка связи 7")/> --->
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.total"=#local.qTotal.cnt#/>
<cfset "out.resultSetSize"=#local.qRead.recordCount#/>
<cfset "out.pageSize"=#arguments.pageSize*1#/>
<cfset "out.page"=#arguments.page*1#/>
<cfset "out.orderBy"=#arguments.orderBy#/>
<cfset var resultCollection=[]/>
<cfloop query=#local.qRead# startRow=#startrow# endRow=#(startrow+maxrows-1)#>
<cfset var rec={}/>
<cfset this.helper.appendRecord(rec, "", local.titleMap, local.qRead, this.helper.snake2camel)/>
<cfset arrayAppend(resultCollection, rec)/>
</cfloop>
<cfset "out.size"=#arrayLen(resultCollection)#/>
<cfset "out.results"=#resultCollection#/>
<cfset "out.runDurationMs"=getTickCount()-request.startTickCount/>
<!---<cfset "out.sql"=#local.result.sql#/>--->
<cfreturn representationOf(out)/>
</cffunction>
</cfcomponent>
</cfcomponent>
+1 -16
View File
@@ -1,11 +1,8 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/serviceOperation/{svcOperationId}">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<cffunction name="get" hint="Операция сервиса"><!--- *** TODO проверка принадлежности тенанту --->
<cfargument name="svcOperationId" type="string" required=true hint="type:integer"/>
<cftry>
@@ -14,7 +11,6 @@
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfquery name="local.qSvcOperation" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
@@ -27,12 +23,10 @@
from svc_operation o
where o.svc_operation_id=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.svcOperationId#" null=#!isValid('integer',arguments.svcOperationId)#/>
</cfquery>
<cfquery name="local.qCfsParam" result="local.result">
select
<m:field_set titleMapOut="local.cfsParamMap" lengthOut="local.fieldCount">
<m:field title="ID" cfSqlType="CF_SQL_INTEGER">p.svc_operation_cfs_param_id</m:field>
<!--- <m:field cfSqlType="CF_SQL_INTEGER">p.svc_operation_id</m:field> --->
<m:field>p.svc_operation_cfs_param</m:field>
<m:field>p.label</m:field>
<m:field>p.data_type</m:field>
@@ -73,18 +67,14 @@
AND p.is_actual
order by p.sort, p.svc_operation_cfs_param_id
</cfquery>
<cfif local.qSvcOperation.recordCount EQ 0>
<cfreturn representationOf(this.helper.formatMessage("Not Found")).withStatus(404)/>
</cfif>
<cfset var out=structNew("linked")/>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount/>
<cfset "out.svcOperation" = this.helper.appendRecord(
structNew("linked"), "", local.titleMap, local.qSvcOperation, this.helper.snake2camel
)/>
<cfset "out.svcOperation.cfsParams"=[]/>
<cfloop query=#local.qCfsParam#>
<cfif (len(local.qCfsParam.func) OR len(local.qCfsParam.expression) OR len(local.qCfsParam.state_path) OR len(local.qCfsParam.nested_ref))
@@ -93,16 +83,13 @@
</cfif>
<cfset arrayAppend(out.svcOperation.cfsParams, this.helper.appendRecord(structNew("linked"), "", local.cfsParamMap, local.qCfsParam, this.helper.snake2camel))/>
</cfloop>
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction>
<!--- *** выглядит ужасно, но красивый вариант не придумывается ряд месяцев --->
<cffunction name="computeDefault">
<cfargument name="defaultCompute"/>
<cfargument name="original"/>
<cfswitch expression=#arguments.defaultCompute#>
<cfcase value="display_name">
</cfcase>
@@ -110,7 +97,5 @@
<cfreturn #arguments.original#/>
</cfdefaultcase>
</cfswitch>
</cffunction>
</cfcomponent>
</cfcomponent>
+1 -42
View File
@@ -1,44 +1,21 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/user">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/><!---*** странно, почему мы его видим?---><!---вынести в апп?--->
</cfsilent>
<!--- *** Внимание! Особая обработка в Application.cfc по имени user.cfc --->
<cffunction name="get" hint="Контрольная информация по текущему пользователю для проверки контекста. Предполагается использовать только для интеграционной отладки. Исключения перехватываются, всегда возвращается код 200, структура ответа при этом не предназначена для разбора">
<cftry>
<cfset var local={}/><!--- *** а точно нужно var? --->
<cfset var out=structNew("linked")/>
<cfset "out.usrId"=#ARGUMENTS.usrId#/>
<!--- <cfset "out.iamServiceUrl"=request.locateIamService()/> --->
<cfset "out.serviceLocationDurationMs"=getTickCount() - request.startTickCount/>
<!--- <cftry>
<cfset var sAuth=listGetAt(request.ORCHESTRATOR_AUTH,2," ")/>
<cfset "out.svcAccount"=listGetAt(toString(ToBinary(sAuth)),1,":")/>
<cfcatch type="any"></cfcatch>
</cftry>
<cfset "out.environmentVariables"=structKeyArray(createObject("java", "java.lang.System").getEnv()).sort("textnocase")/> --->
<!--- <cfdump var=#arguments#/>
<cfabort/> --->
<cfquery name="local.qSpec">
select
<m:field_set titleMapOut="local.specTitleMap" lengthOut="local.fieldCount">
<m:field cfSqlType="CF_SQL_INTEGER">s.specification_id</m:field>
<!--- <m:field>s.specification</m:field>
<m:field>(select count(*) from specification_item i where i.specification_id=s.specification_id) as specification_item_count</m:field> --->
<m:field>c.contract_id</m:field>
<m:field>c.contragent_id</m:field>
<!--- <m:field>c.contract</m:field>
<m:field>to_char(c.dt_contract, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZHTZM') as dt_contract</m:field> --->
</m:field_set>
from specification s
join contract c on (s.contract_id=c.contract_id)
@@ -47,44 +24,26 @@
order by specification_id desc
limit 1;
</cfquery>
<cfquery name="local.qRead" result="local.result">
select
<m:field_set titleMapOut="local.titleMap" lengthOut="local.fieldCount">
<m:field cfSqlType="CF_SQL_INTEGER">u.usr_id</m:field>
<!--- <m:field>u.login</m:field> --->
<m:field>k.contragent_id</m:field>
<!--- <m:field><cfqueryparam cfsqltype="cf_sql_other" value=#arguments.companyUid#/> as arguments_company_uid</m:field> --->
<!--- <m:field>k.external_uid::text as external_uid</m:field> --->
<!--- <m:field>k.contragent</m:field> --->
</m:field_set>
from usr u
left outer join contragent k on u.contragent_id=k.contragent_id
where u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#ARGUMENTS.usrId#/>
</cfquery>
<cfset "out.queryDurationMs"=getTickCount() - request.startTickCount- out.serviceLocationDurationMs/>
<cfset var resultCollection=[]/>
<cfset "out.usr" = (local.qRead.recordCount GT 0) ? this.helper.appendRecord(structNew("linked"), "", local.titleMap, local.qRead, this.helper.snake2camel) : {}/>
<cfset "out.arguments" = #arguments#/>
<cfset "out.defaultSpecification" = (local.qSpec.recordCount GT 0) ? this.helper.appendRecord(structNew("linked"), "", local.specTitleMap, local.qSpec, this.helper.snake2camel) : {}/>
<cfset "out.runDurationMs"=getTickCount()-request.startTickCount/>
<!---<cfset "out.sql"=#local.result.sql#/>--->
<cfreturn representationOf(out)/>
<cfcatch type="any">
<cfreturn representationOf(cfcatch)/>
</cfcatch>
</cftry>
</cffunction>
<!--- <cffunction name="get" hint="проверка">
<cfreturn representationOf("Проверка связи")/>
</cffunction> --->
</cfcomponent>
</cfcomponent>
+1 -62
View File
@@ -1,41 +1,21 @@
<cfcomponent extends="taffy.core.resource" taffy:uri="/instances/{instanceUid}/vault/{secretName}" hint="getting secrets from the vault">
<cfsilent>
<cfimport prefix="m" taglib="../lib"/>
<cfset this.helper=CreateObject("component","lib.rest_api_helper")/>
</cfsilent>
<cffunction name="get" hint="Получить секрет">
<cfargument name="instanceUid" type="string" required=true hint="type:UUID"/>
<cfargument name="secretName" type="string" required=true hint="type:string"/>
<!--- <cfargument name="usrId" type="string" hint="type:integer; no need to provide this argument in request, it is injected by IAM"/> --->
<!--- arguments.usrId неявно вбрасывается из Application.cfm --->
<cftry>
<cfset this.helper.validateField(arguments, "instanceUid", "guid")/>
<cfcatch type="invalidParamValue">
<cfreturn representationOf(this.helper.formatBadRequestError(cfcatch)).withStatus(400)/>
</cfcatch>
</cftry>
<cfset var local={}/>
<cfset local.vaultData={}/>
<!--- <cfset logVaultAccess(
"#request.iam_service_url#/events",
request.auth_header,
request.stand,
arguments.instanceUid,
arguments.secretName,
"",
arguments.login,
arguments.clientID,
arguments.usrId,
arguments.contragentId,
arguments.isImpersonated)/> --->
<!--- *** Добавить проверку, что секрет перечислен в fields - стало быть, доступен пользователю --->
<!--- тут необходимо проконтролировать, что мы смотрим не чужие данные --->
<!--- в других местах мы молча возврашаем пустой резалтсет на запрос чужих данных --->
<!--- варианты: 403, 404, пустой набор данных --->
@@ -59,16 +39,12 @@
order by st.version desc
limit 1
</cfquery>
<cfif local.qCurrentState.recordCount EQ 0>
<cfreturn representationOf("Cannot find instance current state or no permission to view").withStatus(404)/>
</cfif>
<cfif len(local.qCurrentState.instance_data) EQ 0>
<cfreturn representationOf("Instance current state is empty").withStatus(404)/>
</cfif>
<!--- <cfdump var=#local.qCurrentState#/><cfabort/> --->
<cfset var vaultUrl=""/>
<cftry>
<cfset var instanceData=#deserializeJson(local.qCurrentState.instance_data)#/>
@@ -82,7 +58,6 @@
<cfelse>
<cfreturn representationOf("Instance data do not contain vault url").withStatus(422)/>
</cfif>
<cfhttp method="post"
url="#request.vault_login_url#"
timeout="3"
@@ -92,14 +67,11 @@
<cfhttpparam type="HEADER" name="User-Agent" value="#request.USER_AGENT#"/>
<cfhttpparam type="BODY" value='{"role_id":"#request.vault_role_id#","secret_id":"#request.vault_secret_id#"}'/>
</cfhttp>
<cfif local.res.status_code NEQ 200>
<cfreturn representationOf(local.res).withStatus(422)/>
</cfif>
<cfset resData=#deserializeJson(local.res.filecontent)#/>
<cfset client_token=#resData.auth.client_token#/>
<cftry>
<cfhttp method="get"
url="#vaultUrl#"
@@ -112,7 +84,6 @@
<cfif local.cred.status_code NEQ 200>
<cfreturn representationOf(local.cred).withStatus(422)/>
</cfif>
<!--- <cfdump var=#cred#/><cfabort/> --->
<!--- *** костыль для перехода от формата v001 к v002 --->
<cfif structKeyExists(instanceData,"vault")
AND structKeyExists(instanceData.vault,"fields")
@@ -125,18 +96,14 @@
<cfelse>
<cfreturn representationOf("Secret not found or no permission to view").withStatus(404)/>
</cfif>
<cfcatch type="ANY">
<cfreturn representationOf(cfcatch).withStatus(500)/>
</cfcatch>
</cftry>
<cfcatch type="ANY">
<cfreturn representationOf(cfcatch).withStatus(500)/>
</cfcatch>
</cftry>
<!--- логируем успешную попытку доступа. (если логировать сначала - неизвестен результат. *** Можно в catch логировать неуспешный --->
<cfset logVaultAccess(
"#request.iam_service_url#/events",
@@ -150,7 +117,6 @@
arguments.usrId,
arguments.contragentId,
arguments.isImpersonated)/>
<cfset var out = structNew("linked")/>
<cfset "out.name" = #arguments.secretName#/>
<cfset "out.value" = #isSimpleValue(local.vaultData)? local.vaultData : serializeJson(local.vaultData)#/><!--- cast any to string --->
@@ -158,7 +124,6 @@
<cfset "out.runDurationMs"=getTickCount() - request.startTickCount/>
<cfreturn representationOf(out) />
</cffunction>
<cffunction name="logVaultAccess" returntype="void">
<cfargument name="loggerUrl" type="string" required=true hint=""/>
<cfargument name="authHeader" type="string" required=true hint="type:string"/>
@@ -171,25 +136,10 @@
<cfargument name="usrId" type="string" required=true hint="type:string WZ"/>
<cfargument name="contragentId" type="string" required=true hint="type:string WZ"/>
<cfargument name="isImpersonated" type="string" required=true hint="type:boolean"/>
<cftry>
<cfset var local={}/>
<!--- расшифруем некоторые данные пользователя для читабельности --->
<!--- нужно еще учесть имперсонирование --->
<!--- <cfquery name="local.qUsr" result="local.result">
select
u.usr_id
u.login
u.email
k.contragent_id
k.external_uid::text as external_uid
k.contragent
from usr u
left outer join contragent k on (u.contragent_id=k.contragent_id)
where u.usr_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#ARGUMENTS.usrId#/>
</cfquery> --->
<cfset var msg = {
"event_type": "credentials_access",
"ip_address": "#CGI.REMOTE_ADDR#",
@@ -208,7 +158,6 @@
"isImpersonated": "#arguments.isImpersonated#"
}
}/>
<!---
https://cfguide.io/logging-observability
https://www.bennadel.com/blog/4150-writing-to-the-standard-out-console-using-writedump-in-adobe-coldfusion-2021.htm
@@ -218,7 +167,6 @@
<cfset SystemOutput(serializeJson(msg))/><!--- *** lucee specific incompatible syntax --->
<cfcatch type="ANY"></cfcatch>
</cftry>
<cfhttp method="post"
url="#arguments.loggerUrl#"
timeout="3"
@@ -229,18 +177,9 @@
<cfhttpparam type="HEADER" name="Content-Type" value="application/json"/>
<cfhttpparam type="BODY" value="#serializeJson(msg)#"/>
</cfhttp>
<!---
arguments:<cfdump var=#arguments#/>
msg:<cfdump var=#msg#/>
local.res:<cfdump var=#local.res#/>
<cfabort/>
--->
<cfcatch type="any">
<cfrethrow/><!--- на этапе отладки лучше тут падать --->
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>
</cfcomponent>