@appaflytech/zeyrek-ai-mcp 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/knowledge/errorPath.js +48 -5
- package/dist/knowledge/normalize.js +43 -0
- package/dist/knowledge/outlines.js +16 -3
- package/dist/knowledge/rules.js +233 -2
- package/dist/platform/errors.js +76 -1
- package/dist/platform/responses.js +12 -1
- package/dist/resources/examples/function-total.json +6 -1
- package/dist/resources/examples/workflow-call-workflow.json +115 -0
- package/dist/resources/examples/workflow-http-step.json +84 -0
- package/dist/resources/examples/workflow-loop-step.json +132 -0
- package/dist/resources/guides/entity.md +40 -3
- package/dist/resources/guides/getting-started.md +4 -0
- package/dist/resources/guides/microservice.md +40 -0
- package/dist/resources/guides/modify-existing.md +13 -2
- package/dist/resources/guides/workflow.md +173 -12
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/MicroserviceDto.json +972 -0
- package/dist/tools/access.js +1 -1
- package/dist/tools/automations.js +7 -4
- package/dist/tools/entities.js +1 -1
- package/dist/tools/functions.js +1 -1
- package/dist/tools/microservices.js +8 -4
- package/dist/tools/platformInfo.js +1 -1
- package/dist/tools/workflows.js +6 -2
- package/package.json +2 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "workflow adımı: bir akışın içinden BAŞKA bir kayıtlı akışı çağırır (alt akış). İki şey burada tuzaktır ve ikisi de ölçüldü: hedef ADLA değil rule.workflowId ile (GUID) seçilir, ve alt akışın girdisi YALNIZ rule.expression ile bağlanır — payloadExpression / customParameters kaydedilir ama SESSİZCE yok sayılır. Bu akış lokal stack'te koşturuldu (test projesi, development, 2026-09-16): iki yol da ayrı ayrı ölçüldü.",
|
|
5
|
+
"harvestedFrom": "canlı ölçüm 2026-09-16 (workflow_preview, test/development) + kod: zeyrek-backend WorkflowStep.cs:22-49, StepRule.cs:23,79-80"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"hedef": "rule.workflowId bir GUID'dir — slug ya da ad DEĞİL. Id'yi workflow_list ile alın. Alt akış aynı application içinde kayıtlı olmalıdır (preview'daki kaydedilmemiş bir akış çağrılamaz).",
|
|
9
|
+
"girdi": "GİRDİ YALNIZ rule.expression İLE BAĞLANIR ve hedefin parameter DTO'sunun şeklini üretmelidir: \"({ orderId: load_order.data.id })\". Yürütücü ifadeyi yalnız hedef akışın parameterDtoId'si VARSA değerlendirir (WorkflowStep.cs:27-31).",
|
|
10
|
+
"sessizDusme": "ÖLÇÜLDÜ 2026-09-16: aynı adıma payloadExpression yazıldığında alan kaydedilir, istek 200 döner, adım hatasız görünür — ama alt akış GİRDİSİZ koşar. Preview'da kanıtı şudur: adımın input satırı yalnız { source: 'workflow', member: 'parameter' } olur, expression ve value alanları HİÇ GELMEZ. customParameters da aynı şekilde yok sayılır (36/S-29).",
|
|
11
|
+
"adAyniAlanUcAnlam": "rule.expression alanı üç adımda ÜÇ FARKLI şey demektir ve adı yanıltır: workflow adımında GİRDİ EŞLEMESİ, code adımında GÖVDE, query adımında PARAMETRE BAĞLAMA. Bir code adımından kopyalayıp workflow adımına yapıştırmayın.",
|
|
12
|
+
"cikti": "Alt akışın response gövdesi bu adımın çıktısı olur: <adım>.data. Ayrı bir 'çıktı değişkeni' alanı yoktur. İfade bağlamında adım { data, meta, error } şeklindedir (http adımıyla aynı sarmal).",
|
|
13
|
+
"ciktiyiDtoBicer": "ÖLÇÜLDÜ: alt akışın returnDtoId'si çıktıyı BİÇER. Alt akış bodyExpression'ında DTO'da olmayan bir anahtar dönerse o anahtar yanıttan sessizce düşer — çağıranda eksik alan görürseniz önce alt akışın return DTO'suna bakın.",
|
|
14
|
+
"hata": "Alt akış patlarsa bu adım fail dalına gider ve alt akışın son hatalı adımının istisnası taşınır (WorkflowStep.cs:49).",
|
|
15
|
+
"preview": "workflow_preview alt akışı children[] içinde kind:'subWorkflow' olarak TAM adım iziyle döner — girdinin gerçekten bağlanıp bağlanmadığını oradan görürsünüz."
|
|
16
|
+
},
|
|
17
|
+
"calleeDefinition": {
|
|
18
|
+
"name": "Fatura Oluştur",
|
|
19
|
+
"slug": "fatura-olustur",
|
|
20
|
+
"description": "Sipariş id'siyle fatura kaydı açar ve fatura numarasını döner.",
|
|
21
|
+
"type": "workflow",
|
|
22
|
+
"parameterDtoId": "<girdi DTO id: { orderId: string }>",
|
|
23
|
+
"parameterBehavior": "object",
|
|
24
|
+
"returnDtoId": "<çıktı DTO id: { faturaNo: string }>",
|
|
25
|
+
"returnBehavior": "object",
|
|
26
|
+
"steps": [
|
|
27
|
+
{
|
|
28
|
+
"id": "<GUID-C1>",
|
|
29
|
+
"name": "Start",
|
|
30
|
+
"type": "start",
|
|
31
|
+
"description": "Alt akışı başlatır.",
|
|
32
|
+
"branches": [{ "id": "<GUID-C2>", "name": "start", "type": "success", "nextStepId": "<GUID-C3>" }]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "<GUID-C3>",
|
|
36
|
+
"name": "Response",
|
|
37
|
+
"type": "response",
|
|
38
|
+
"description": "Üretilen fatura numarasını döner; çağıran bunu <adım>.data ile okur.",
|
|
39
|
+
"rule": {
|
|
40
|
+
"stepType": "response",
|
|
41
|
+
"bodyExpression": "({ faturaNo: 'FTR-' + input.orderId })",
|
|
42
|
+
"httpStatusCode": 200
|
|
43
|
+
},
|
|
44
|
+
"branches": []
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"definition": {
|
|
49
|
+
"name": "Sipariş Kapat",
|
|
50
|
+
"slug": "siparis-kapat",
|
|
51
|
+
"description": "Siparişi kapatır ve fatura akışını çağırarak fatura numarasını döner.",
|
|
52
|
+
"type": "workflow",
|
|
53
|
+
"parameterDtoId": "<girdi DTO id: { orderId: string }>",
|
|
54
|
+
"parameterBehavior": "object",
|
|
55
|
+
"returnDtoId": "<çıktı DTO id: { faturaNo: string }>",
|
|
56
|
+
"returnBehavior": "object",
|
|
57
|
+
"steps": [
|
|
58
|
+
{
|
|
59
|
+
"id": "<GUID-1 üret: crypto.randomUUID()>",
|
|
60
|
+
"name": "Start",
|
|
61
|
+
"type": "start",
|
|
62
|
+
"description": "Akışı başlatır.",
|
|
63
|
+
"branches": [
|
|
64
|
+
{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" },
|
|
65
|
+
{ "id": "<GUID-9>", "name": "global-error", "type": "global-error", "nextStepId": "<GUID-8>" }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "<GUID-3>",
|
|
70
|
+
"name": "fatura_akisi",
|
|
71
|
+
"type": "workflow",
|
|
72
|
+
"description": "Fatura oluşturma akışını çağırır; sipariş id'sini girdi olarak geçer.",
|
|
73
|
+
"rule": {
|
|
74
|
+
"stepType": "workflow",
|
|
75
|
+
"workflowId": "<workflow_list'ten alınan hedef akış id'si>",
|
|
76
|
+
"expression": "({ orderId: input.orderId })"
|
|
77
|
+
},
|
|
78
|
+
"branches": [
|
|
79
|
+
{ "id": "<GUID-4>", "name": "success", "type": "success", "nextStepId": "<GUID-6>" },
|
|
80
|
+
{ "id": "<GUID-5>", "name": "fail", "type": "fail", "nextStepId": null }
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "<GUID-6>",
|
|
85
|
+
"name": "Response",
|
|
86
|
+
"type": "response",
|
|
87
|
+
"description": "Alt akışın ürettiği fatura numarasını 200 ile döner.",
|
|
88
|
+
"rule": {
|
|
89
|
+
"stepType": "response",
|
|
90
|
+
"bodyExpression": "({ faturaNo: fatura_akisi.data.faturaNo })",
|
|
91
|
+
"httpStatusCode": 200
|
|
92
|
+
},
|
|
93
|
+
"branches": []
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "<GUID-8>",
|
|
97
|
+
"name": "onError",
|
|
98
|
+
"type": "response",
|
|
99
|
+
"description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
|
|
100
|
+
"rule": {
|
|
101
|
+
"stepType": "response",
|
|
102
|
+
"bodyExpression": "({ code: error.code, message: error.message })",
|
|
103
|
+
"httpStatusCode": 500
|
|
104
|
+
},
|
|
105
|
+
"branches": []
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"usage": {
|
|
110
|
+
"kurulumSirasi": "1) Alt akışın girdi/çıktı DTO'larını aç, 2) alt akışı workflow_create ile KAYDET, 3) dönen id'yi çağıranın rule.workflowId alanına yaz. Kaydedilmemiş bir akış çağrılamaz.",
|
|
111
|
+
"girdiKontrolu": "workflow_preview çıktısında çağıran adımın input satırına bakın: expression ve value doluysa girdi bağlanmıştır. Yalnız { source: 'workflow', member: 'parameter' } görüyorsanız girdiniz YOK SAYILDI — büyük olasılıkla payloadExpression yazdınız, rule.expression'a taşıyın.",
|
|
112
|
+
"yanlisYol": "YANLIŞ: rule: { stepType: 'workflow', workflowId: '…', payloadExpression: \"({ orderId: 1 })\" } — kaydedilir, 200 döner, alt akış girdisiz koşar. DOĞRU: aynı ifadeyi rule.expression'a koyun.",
|
|
113
|
+
"dogrulama": "workflow_preview; yanıtta children[] içinde kind:'subWorkflow' girişinin input alanı beklediğiniz nesne olmalı."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "http adımı: dış (ya da platformun kendi) HTTP ucunu çağırır. Adımın üç ayrı alanı vardır — urlExpression (JS ifadesi, STRING üretir), httpMethod, contentType — ve gövde AYRI BİR ALAN DEĞİLDİR: payloadExpression tek bir ifadeyle { header, body, queryString } ÜÇÜNÜ BİRDEN taşımak zorundadır. Bu akış lokal stack'te koşturuldu (test projesi, development, 2026-09-16): platformun kendi table ucu çağrıldı, 200 döndü ve çıktı şekli ölçüldü.",
|
|
5
|
+
"harvestedFrom": "canlı ölçüm 2026-09-16 (workflow_preview, test/development) + kod: zeyrek-backend HttpStep.cs:26-99,143-158, StepRule.cs:47-51"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"payloadExpression": "ÜÇ ANAHTAR DA ZORUNLUDUR, boş olsalar bile: ({ body: {}, header: {}, queryString: {} }). Yürütücü üçüne de C# dynamic üye erişimiyle bakar (HttpStep.cs:32,42,59), eksik anahtar adımı RuntimeBinderException ile düşürür. Ölçülen mesaj birebir: \"'System.Dynamic.ExpandoObject' does not contain a definition for 'queryString'\". Bu bir doğrulama hatası değildir; adım fail dalına gider.",
|
|
9
|
+
"urlExpression": "Bu bir İFADEDİR, düz metin değil: sabit adres tırnak içinde yazılır ('https://api.example.com/v1/rates'), dinamik adres ifadeyle kurulur ('https://api.example.com/orders/' + input.orderId). Tırnaksız yazarsan JS onu değişken sanır ve adım 'x is not defined' ile düşer.",
|
|
10
|
+
"output": "ÖLÇÜLDÜ 2026-09-16 — ŞEMAYLA ÇELİŞİR. workflow_step_schema('http') returnScheme'i kök seviyede { httpStatusCode, data } ilan ediyor, AMA ifade bağlamında adım { data, meta: { elapsedTime, httpStatusCode, totalCount }, error } şeklindedir: gövde <adım>.data, durum kodu <adım>.meta.httpStatusCode. <adım>.httpStatusCode UNDEFINED döner. Şemaya göre yazarsan sessizce boş değer alırsın.",
|
|
11
|
+
"responseParsing": "Yanıt gövdesi JSON'a benziyorsa ayrıştırılır, benzemiyorsa SESSİZCE boş nesne ({}) olur (HttpStep.cs:92) — XML dönen uçtan veri alamazsın ve hata da almazsın (36/W-2, açık bulgu). Ham metne erişmenin yolu yoktur.",
|
|
12
|
+
"contentType": "KOD OKUMASINDAN (bu koşuda ölçülmedi): 'application/json' → JsonContent; 'application/x-www-form-urlencoded' → düz anahtar/değer; BAŞKA BİR DEĞER ya da BOŞ → gövde body.ToString() ile gider ve nesne gövdesi için bu, tip adının kendisidir (HttpStep.cs:63-85). POST/PUT'ta contentType'ı AÇIKÇA yazın.",
|
|
13
|
+
"stepName": "Adım çıktısına ADIM ADIYLA erişilir, o yüzden ad geçerli bir JS tanımlayıcısı olmalıdır. Boşluklu ad ('Call Roles') ifadede çağrılamaz; ölçümde call_roles is not defined ile düştü. snake_case kullanın.",
|
|
14
|
+
"authorization": "Token gerekiyorsa header anahtarına koyun: header: { Authorization: 'Bearer ' + system.token }. Adım platformun kendi kimliğini OTOMATİK taşımaz."
|
|
15
|
+
},
|
|
16
|
+
"definition": {
|
|
17
|
+
"name": "Kur Çek",
|
|
18
|
+
"slug": "kur-cek",
|
|
19
|
+
"description": "Dış servisten güncel kuru çeker ve gövdesini döner.",
|
|
20
|
+
"type": "workflow",
|
|
21
|
+
"returnDtoId": "<çıktı DTO id: { kur: number }>",
|
|
22
|
+
"returnBehavior": "object",
|
|
23
|
+
"steps": [
|
|
24
|
+
{
|
|
25
|
+
"id": "<GUID-1 üret: crypto.randomUUID()>",
|
|
26
|
+
"name": "Start",
|
|
27
|
+
"type": "start",
|
|
28
|
+
"description": "Akışı başlatır.",
|
|
29
|
+
"branches": [
|
|
30
|
+
{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" },
|
|
31
|
+
{ "id": "<GUID-9>", "name": "global-error", "type": "global-error", "nextStepId": "<GUID-8>" }
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "<GUID-3>",
|
|
36
|
+
"name": "kur_servisi",
|
|
37
|
+
"type": "http",
|
|
38
|
+
"description": "Kur servisini çağırır; yanıtın gövdesi bir sonraki adıma geçer.",
|
|
39
|
+
"caution": "Dış bağımlılık: servis yavaşlarsa akış bekler, XML dönerse çıktı sessizce boş nesne olur.",
|
|
40
|
+
"rule": {
|
|
41
|
+
"stepType": "http",
|
|
42
|
+
"urlExpression": "'http://zeyrek-test.zeyrek.local/api/test/m/test/olcum-roller'",
|
|
43
|
+
"httpMethod": "GET",
|
|
44
|
+
"contentType": "application/json",
|
|
45
|
+
"payloadExpression": "({ body: {}, header: { 'X-Environment': 'development' }, queryString: {} })"
|
|
46
|
+
},
|
|
47
|
+
"branches": [
|
|
48
|
+
{ "id": "<GUID-4>", "name": "success", "type": "success", "nextStepId": "<GUID-6>" },
|
|
49
|
+
{ "id": "<GUID-5>", "name": "fail", "type": "fail", "nextStepId": null }
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "<GUID-6>",
|
|
54
|
+
"name": "Response",
|
|
55
|
+
"type": "response",
|
|
56
|
+
"description": "Çekilen gövdeyi ve HTTP durum kodunu 200 ile döner.",
|
|
57
|
+
"rule": {
|
|
58
|
+
"stepType": "response",
|
|
59
|
+
"bodyExpression": "({ veri: kur_servisi.data, durum: kur_servisi.meta.httpStatusCode })",
|
|
60
|
+
"httpStatusCode": 200
|
|
61
|
+
},
|
|
62
|
+
"branches": []
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "<GUID-8>",
|
|
66
|
+
"name": "onError",
|
|
67
|
+
"type": "response",
|
|
68
|
+
"description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
|
|
69
|
+
"rule": {
|
|
70
|
+
"stepType": "response",
|
|
71
|
+
"bodyExpression": "({ code: error.code, message: error.message })",
|
|
72
|
+
"httpStatusCode": 500
|
|
73
|
+
},
|
|
74
|
+
"branches": []
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"usage": {
|
|
79
|
+
"postGovdesi": "POST için: { stepType: 'http', urlExpression: \"'https://api.example.com/orders'\", httpMethod: 'POST', contentType: 'application/json', payloadExpression: \"({ body: { orderId: input.orderId, adet: 2 }, header: { Authorization: 'Bearer ' + system.token }, queryString: {} })\" }",
|
|
80
|
+
"queryStringKullanimi": "Sorgu parametresi payloadExpression'ın queryString anahtarına konur, URL'e elle eklenmez: queryString: { from: 'USD', to: 'TRY' }.",
|
|
81
|
+
"onlemliOkuma": "Gövdeyi <adım>.data ile, durum kodunu <adım>.meta.httpStatusCode ile okuyun; şemanın vaat ettiği <adım>.httpStatusCode undefined'dır (ölçüldü 2026-09-16).",
|
|
82
|
+
"dogrulama": "workflow_preview ile koşturun ve yanıttaki steps[] içinde http adımının input satırlarına bakın: source:'url' ve source:'payload' iki ayrı satır olarak görünür; hata varsa runtimeErrors alanı adımı adıyla sayar."
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "loop adımı: bir dizinin her öğesi için TEK bir çocuk adım koşturur. Üç şey tuzaktır ve üçü de ölçüldü: çocuk kuralının alanı TEKİLDİR (rule.stepRule, 'children' değil), çocuğun tipi loop'un kendi rule.stepType alanında yazar, ve geçerli öğeye erişim aracı olan loopItem YALNIZ loop'un kendi değerlendirdiği ifadelerde vardır — code çocuğunda YOKTUR. Lokal stack'te koşturuldu (test projesi, development, 2026-09-16).",
|
|
5
|
+
"harvestedFrom": "canlı ölçüm 2026-09-16 (workflow_preview, test/development) + kod: zeyrek-backend LoopStep.cs:38,45-56,73-77, WorkflowDto.cs:399"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"cocukAlaniTekil": "Çocuğun kural gövdesi rule.stepRule alanına konur — TEKİL. 'children' yazarsan domain tarafı boş kalır, çocuk null kuralla koşar ve loop NullReferenceException verir; hata mesajı yanıltıcı biçimde arrayExpression'ı suçlar (WF-018 lint'i bunu istek gitmeden keser).",
|
|
9
|
+
"cocukTipi": "Çocuğun tipi loop'un KENDİ rule.stepType alanındadır ('table', 'code', 'http'…), stepRule'un içinde değil. stepRule kendi stepType'ını da taşır; ikisini aynı yazın.",
|
|
10
|
+
"dizi": "rule.arrayExpression diziyi üreten bir JS ifadesidir: 'input.kalemler || []'. Boş bırakılamaz (WF-018 block). Dizi değilse ya da null ise döngü koşmaz.",
|
|
11
|
+
"loopItem": "ÖLÇÜLDÜ 2026-09-16 — SINIRLI. Geçerli öğe loopItem adıyla YALNIZ loop'un kendi değerlendirdiği ifadelerde görünür: table çocuğunun filtre ve alan ifadeleri bunu görür (ölçüldü: filtre 'loopItem.slug' → 'super-admin'). AMA code çocuğu kendi JS motorunu kurar ve orada loopItem TANIMSIZDIR: adım 'Error: loopItem is not defined' ile düşer (motorun globalleri input · system · file · crypto · convert · Log · UtcNow …). Her öğe için hesap yapmanız gerekiyorsa hesabı arrayExpression'da önden yapın ya da çocuğu table/http adımı olarak kurun.",
|
|
12
|
+
"filtredeAlias": "Çocuk table adımının filtresi alias TAŞIMAK ZORUNDADIR ('alias': '<entity alias>', 'path': ''). Alias'sız filtre SESSİZCE DÜŞER: ifade doğru değerlendirilir (preview input satırında loopItem.kod'un değeri görünür) ama sorguya uygulanmaz ve her yineleme AYNI ilk satırı döner — hata yoktur, veri yanlıştır. Ölçüldü 2026-09-16: alias'sız iki yineleme de ilk rolü döndü, alias eklenince her yineleme kendi satırını döndü. Aynı tuzak düz table adımında da geçerlidir (platform_guide('workflow') §'Table adımının filtresi').",
|
|
13
|
+
"indeksYok": "İndeks değişkeni YOKTUR — yalnız loopItem vardır. Sıra numarası gerekiyorsa diziyi arrayExpression'da üretin: 'input.kalemler.map((k, i) => ({ ...k, sira: i }))' ve çocukta loopItem.sira kullanın.",
|
|
14
|
+
"cikti": "Çıktı [{ item, result }] dizisidir: item o yinelemenin öğesi, result çocuğun o yinelemedeki çıktısı. İfadede <adım>.data ile okunur; sonuçları düzleştirmek için <adım>.data.map(r => r.result). Bir yineleme düşerse o girişte result HİÇ OLUŞMAZ, yalnız item kalır.",
|
|
15
|
+
"preview": "workflow_preview her yinelemeyi children[] içinde kind:'loopIteration' olarak ayrı ayrı gösterir — hangi öğede düştüğünü oradan görürsünüz."
|
|
16
|
+
},
|
|
17
|
+
"definition": {
|
|
18
|
+
"name": "Kalemleri İşle",
|
|
19
|
+
"slug": "kalemleri-isle",
|
|
20
|
+
"description": "Gelen kalem listesinin her satırı için stok kaydını okur ve sonuçları toplu döner.",
|
|
21
|
+
"type": "workflow",
|
|
22
|
+
"parameterDtoId": "<girdi DTO id: { kalemler: array }>",
|
|
23
|
+
"parameterBehavior": "object",
|
|
24
|
+
"returnDtoId": "<çıktı DTO id: { sonuclar: array }>",
|
|
25
|
+
"returnBehavior": "object",
|
|
26
|
+
"steps": [
|
|
27
|
+
{
|
|
28
|
+
"id": "<GUID-1 üret: crypto.randomUUID()>",
|
|
29
|
+
"name": "Start",
|
|
30
|
+
"type": "start",
|
|
31
|
+
"description": "Akışı başlatır.",
|
|
32
|
+
"branches": [
|
|
33
|
+
{
|
|
34
|
+
"id": "<GUID-2>",
|
|
35
|
+
"name": "start",
|
|
36
|
+
"type": "success",
|
|
37
|
+
"nextStepId": "<GUID-3>"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "<GUID-9>",
|
|
41
|
+
"name": "global-error",
|
|
42
|
+
"type": "global-error",
|
|
43
|
+
"nextStepId": "<GUID-8>"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "<GUID-3>",
|
|
49
|
+
"name": "her_kalem",
|
|
50
|
+
"type": "loop",
|
|
51
|
+
"description": "Her kalem için stok satırını okur; kalem koduna loopItem ile erişir.",
|
|
52
|
+
"caution": "Çocuk adım tek bir tanedir ve her yinelemede ayrı koşar; büyük listede tur sayısı kadar sorgu demektir.",
|
|
53
|
+
"rule": {
|
|
54
|
+
"stepType": "table",
|
|
55
|
+
"arrayExpression": "input.kalemler || []",
|
|
56
|
+
"stepRule": {
|
|
57
|
+
"stepType": "table",
|
|
58
|
+
"entityId": "<stoklar entity id>",
|
|
59
|
+
"datasourceId": "<datasource id>",
|
|
60
|
+
"operation": "get",
|
|
61
|
+
"isSingle": true,
|
|
62
|
+
"alias": "stoklar",
|
|
63
|
+
"selectFields": [
|
|
64
|
+
{
|
|
65
|
+
"alias": "stoklar",
|
|
66
|
+
"fieldSlug": "adet",
|
|
67
|
+
"path": "",
|
|
68
|
+
"fieldId": "<adet alanı id>"
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"filters": [
|
|
72
|
+
{
|
|
73
|
+
"alias": "stoklar",
|
|
74
|
+
"path": "",
|
|
75
|
+
"fieldSlug": "kod",
|
|
76
|
+
"operator": "eq",
|
|
77
|
+
"value": "loopItem.kod",
|
|
78
|
+
"fieldId": "<kod alanı id>"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"joins": []
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"branches": [
|
|
85
|
+
{
|
|
86
|
+
"id": "<GUID-4>",
|
|
87
|
+
"name": "success",
|
|
88
|
+
"type": "success",
|
|
89
|
+
"nextStepId": "<GUID-6>"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"id": "<GUID-5>",
|
|
93
|
+
"name": "fail",
|
|
94
|
+
"type": "fail",
|
|
95
|
+
"nextStepId": null
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "<GUID-6>",
|
|
101
|
+
"name": "Response",
|
|
102
|
+
"type": "response",
|
|
103
|
+
"description": "Her kalemin okunan stok sonucunu tek listede 200 ile döner.",
|
|
104
|
+
"rule": {
|
|
105
|
+
"stepType": "response",
|
|
106
|
+
"bodyExpression": "({ sonuclar: her_kalem.data.map(r => ({ kod: r.item.kod, adet: r.result.adet })) })",
|
|
107
|
+
"httpStatusCode": 200
|
|
108
|
+
},
|
|
109
|
+
"branches": []
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "<GUID-8>",
|
|
113
|
+
"name": "onError",
|
|
114
|
+
"type": "response",
|
|
115
|
+
"description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
|
|
116
|
+
"rule": {
|
|
117
|
+
"stepType": "response",
|
|
118
|
+
"bodyExpression": "({ code: error.code, message: error.message })",
|
|
119
|
+
"httpStatusCode": 500
|
|
120
|
+
},
|
|
121
|
+
"branches": []
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"usage": {
|
|
126
|
+
"olculenCikti": "[{ item: { kod: 'K-1' }, result: { adet: 12 } }, …] — item öğe, result çocuğun çıktısı.",
|
|
127
|
+
"indeksEklemek": "arrayExpression: \"(input.kalemler || []).map((k, i) => ({ ...k, sira: i }))\" ve çocukta loopItem.sira.",
|
|
128
|
+
"codeCocugu": "code çocuğunda loopItem YOKTUR (ölçüldü). Her öğe için hesap gerekiyorsa hesabı arrayExpression'a taşıyın: \"(input.kalemler || []).map(k => ({ ...k, tutar: k.fiyat * k.adet }))\" — böylece çocuk yalnız yazma/okuma yapar ve loopItem.tutar'a bakar.",
|
|
129
|
+
"dogrulama": "workflow_preview; yanıtta loop adımının output'u [{item, result}] dizisi olmalı ve children[] her yineleme için bir kind:'loopIteration' girişi taşımalı. Bir girişte result yoksa o yineleme düşmüştür — runtimeErrors alanına bakın.",
|
|
130
|
+
"aliasTuzagi": "Filtreye alias koymayı unutursanız döngü hatasız koşar ve HER yineleme aynı satırı döner; preview'da fark etmek için children[] içindeki her yinelemenin output'unu karşılaştırın."
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -105,9 +105,11 @@ nullable, gateway'den görünmez:
|
|
|
105
105
|
|
|
106
106
|
**İki farklı anahtar, karıştırma:**
|
|
107
107
|
|
|
108
|
-
- **id** → `page_create/page_update` ve `microservice_create` `roleIds`
|
|
109
|
-
|
|
110
|
-
`
|
|
108
|
+
- **id** → `page_create/page_update` ve `microservice_create` `roleIds` alanı; `role_list`ten okunur.
|
|
109
|
+
**Yazma gövdesinde alan adı `roleIds`, okuma şeklinde `roles`** (`{ id, name: '<slug>' }`) — ve
|
|
110
|
+
`microservice_patch` **okuma şeklini** yamalar, yani orada yol `/roles`'tır
|
|
111
|
+
(`platform_guide('microservice')` §"Patch belgesi okuma şeklidir"). `microservice.roles`
|
|
112
|
+
middleware'de de bu runtime şeklidir.
|
|
111
113
|
- **slug** → `Authorized(user.roles)`, middleware'in kurduğu `user.roles` / `Page.user.roles`.
|
|
112
114
|
Platformun get-user sorgusu `roles`'u `user_roles` üzerinden **slug dizisi** olarak üretir
|
|
113
115
|
(`SELECT … ARRAY(SELECT r.slug FROM roles r INNER JOIN user_roles ur …)`), auth middleware
|
|
@@ -233,6 +235,38 @@ olduğu için serbest metindir; `course_assignments.semester` (Güz/Bahar/Yaz) e
|
|
|
233
235
|
tablodaki değerler `valueOptions` içinde yoksa migration `check constraint ... is violated` ile
|
|
234
236
|
düşer. Sıra: **önce satırları kanonik değerlere taşı, sonra alanı enum yap.**
|
|
235
237
|
|
|
238
|
+
### Tek bir enum değerini ekle / sil / yeniden adlandır
|
|
239
|
+
|
|
240
|
+
**Listeyi yeniden göndermek gerekmez.** `entity_patch` düz RFC 6902 op'ları taşır ve seçici
|
|
241
|
+
(`[slug=…]`, `[value=…]`) `valueOptions` derinliğinde de çözülür:
|
|
242
|
+
|
|
243
|
+
```jsonc
|
|
244
|
+
// ekle — dizinin sonuna; "-" JSON Pointer'ın "diziye ekle" belirtecidir
|
|
245
|
+
{ "op": "add", "path": "/fields[slug=status]/valueOptions/-",
|
|
246
|
+
"value": { "value": "Onayda", "icon": "#0052CC", "order": 4 } }
|
|
247
|
+
|
|
248
|
+
// sil
|
|
249
|
+
{ "op": "remove", "path": "/fields[slug=status]/valueOptions[value=İptal]" }
|
|
250
|
+
|
|
251
|
+
// yeniden adlandır (etiketi değiştir, sırayı koru)
|
|
252
|
+
{ "op": "replace", "path": "/fields[slug=status]/valueOptions[value=İptal]/value", "value": "Reddedildi" }
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- **`isEnum` türetilmiştir** (`ValueOptions.Any()`), patch'te ayrıca yazılmaz; alan zaten enum
|
|
256
|
+
olduğu sürece tek op yeter.
|
|
257
|
+
- **Düz metin alanını enum'a çevirmek bu yol DEĞİLDİR:** `valueOptions` boş/null ise `/-` yolu
|
|
258
|
+
düşer — tam diziyi `replace` ile yaz (`/fields[slug=status]/valueOptions`).
|
|
259
|
+
- `order`'ı ekleme sırasında ver; verilmezse `0` olur ve sıralı raporlar sessizce bozulur
|
|
260
|
+
(yukarıdaki `order` maddesi).
|
|
261
|
+
- Çok dilli etiket gerekiyorsa `labels` aynı op'un `value` nesnesine girer.
|
|
262
|
+
- **Sil ve yeniden adlandırmadan ÖNCE satırları taşı.** Değeri hâlâ taşıyan satır varsa istek
|
|
263
|
+
**reddedilir ve hiçbir şey yazılmaz** — ölçüldü 2026-09-16, mesaj okunabilir: `Migration failed.
|
|
264
|
+
Please try again. Details: check constraint "check_<field id>" of relation "<tablo>" is violated
|
|
265
|
+
by some row`. Platform eski değeri satırlarda yeniden adlandırmaz. Sıra: `row_update` ile kanonik
|
|
266
|
+
değere taşı → sonra enum op'unu gönder (aynı sırayla ölçüldü: taşıma sonrası `remove` geçti).
|
|
267
|
+
- Yanıtın outline'ı alanın yeni değer listesini (`values`, ilk 30 + `valueOptionCount`) taşır —
|
|
268
|
+
ikinci bir `entity_get` turu gerekmez.
|
|
269
|
+
|
|
236
270
|
## Artan kod alanı (`Incremental`)
|
|
237
271
|
|
|
238
272
|
Sıralı, önekli, insan okuyabilir kod üretir (`TLP-0001`). Alan tanımı:
|
|
@@ -1070,3 +1104,6 @@ gönderme.
|
|
|
1070
1104
|
`version` bir **string**tir (mikrosaniye zaman damgası, `Number.MAX_SAFE_INTEGER`'ın üstünde):
|
|
1071
1105
|
sayıya çevirirsen yuvarlanır ve her patch 409 döner. Yanıttaki `newVersion` bir sonraki patch'in
|
|
1072
1106
|
`baseVersion`'ıdır.
|
|
1107
|
+
|
|
1108
|
+
Seçim listesine (enum) tek değer eklemek, silmek ya da yeniden adlandırmak da bu yoldan gider ve
|
|
1109
|
+
listeyi yeniden göndermeyi **gerektirmez** — §"Tek bir enum değerini ekle / sil / yeniden adlandır".
|
|
@@ -56,6 +56,10 @@ Bağımlılıklar bu sırayı zorunlu kılar; atlama:
|
|
|
56
56
|
ifadelerde Functions.<ad>(…) ile, akışlar 'function' adımıyla çağırır;
|
|
57
57
|
sayfaya özgü fonksiyon (scope:'page') sayfa açıldıktan sonra gelir.
|
|
58
58
|
Kurallar platform_guide('functions'))
|
|
59
|
+
function_update (GÖVDE DEĞİŞİNCE: function_update { id, body } — patch aracı YOKTUR,
|
|
60
|
+
fonksiyonun tek büyük alanı gövdedir ve gateway vermediğin alanları
|
|
61
|
+
mevcut kayıttan doldurur. Adı değiştirirsen platform çağıranları AYNI
|
|
62
|
+
işlemde yeni ada taşır (renamedReferences))
|
|
59
63
|
11. layout_create (header/sidebar + MUTLAKA bir 'slot' — slot'suz layout'a bağlı sayfa
|
|
60
64
|
boş görünür; bkz. platform_guide('page') Kural 5)
|
|
61
65
|
12. page_create + … (İŞ BURADA BİTMEZ: her kaydın yönetim ekranı, kimlik varsa giriş
|
|
@@ -620,6 +620,45 @@ gerekmese bile** ona bir middleware bağla. Tek `variable` adımlı, hiçbir şe
|
|
|
620
620
|
Zayıf alternatif: bütün doğrulamaları ilk yazmadan **önce** bitirip yazma bölümünde hiç
|
|
621
621
|
`response`/hata üretmemek — beklenmedik istisnaya karşı korumasızdır.
|
|
622
622
|
|
|
623
|
+
## Patch belgesi OKUMA şeklidir — rol listesi `/roles` yolundadır (MS-022)
|
|
624
|
+
|
|
625
|
+
`microservice_patch` yamayı **`microservice_get`'in döndürdüğü gövdeye** uygular; platform da onu
|
|
626
|
+
kendi tarafında `MicroserviceDto` olarak okuyup `UpdateMicroserviceCommand`'a çevirir
|
|
627
|
+
(`PatchMicroserviceCommand.cs:36-55`). Yani yamalanan belge **yazma komutu değildir** ve iki
|
|
628
|
+
belgenin alan adları her yerde aynı değildir:
|
|
629
|
+
|
|
630
|
+
| Ne | Create/update GÖVDESİ | Patch YOLU (okuma şekli) |
|
|
631
|
+
|---|---|---|
|
|
632
|
+
| Rol listesi | `roleIds: [<guid>]` | **`/roles`** → `[{ id, name }]` |
|
|
633
|
+
| Middleware listesi | `middlewareIds: [<guid>]` | `/middlewareIds` (aynı) |
|
|
634
|
+
| Alan listesi | `sourceParameters.selectFields` | `/sourceParameters/selectFields` (aynı) |
|
|
635
|
+
|
|
636
|
+
**Ucun rolünü değiştirmek için tanımı yeniden göndermek gerekmez** — tek op yeter:
|
|
637
|
+
|
|
638
|
+
```jsonc
|
|
639
|
+
// tüm listeyi yaz (tercih edilen): name isteğe bağlıdır, platform yalnız id okur
|
|
640
|
+
{ "op": "replace", "path": "/roles", "value": [{ "id": "<role_list id>" }] }
|
|
641
|
+
|
|
642
|
+
// listeye ekle
|
|
643
|
+
{ "op": "add", "path": "/roles/-", "value": { "id": "<role_list id>" } }
|
|
644
|
+
|
|
645
|
+
// tek rolü çıkar
|
|
646
|
+
{ "op": "remove", "path": "/roles[id=<role_list id>]" }
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
- **`roleIds` yolu patch belgesinde YOKTUR.** Verirsen gateway `/roles`'a çevirir, çıplak guid'leri
|
|
650
|
+
`{ id }` nesnesine sarar ve çeviriyi `gatewayNormalized` ile söyler — istek düşmez. Çevrilmeseydi
|
|
651
|
+
`replace /roleIds` gateway'de `OPERATION_PATH_UNRESOLVABLE` verirdi, `add /roleIds` ise platformda
|
|
652
|
+
**sessizce yutulurdu** (bilinmeyen üye atılır, 200 döner, rol değişmez).
|
|
653
|
+
- **Tam dizi `replace`'i tercih et.** Rolü olmayan uçta alan `null` serileşebilir ve `/roles/-`
|
|
654
|
+
çözülemez; gateway bunu tek öğelik tam diziye katlar ve notu yanıta yazar.
|
|
655
|
+
- Rol id'leri `role_list`ten gelir. `role_list` **slug döndürmez** ve iki anahtar farklıdır:
|
|
656
|
+
buradaki `id` ile yetkilendirmenin karşılaştırdığı `slug` aynı şey değildir
|
|
657
|
+
(`platform_guide('entity')` §"Hazır entity'ler").
|
|
658
|
+
- Yazmanın kanıtı yanıttadır: outline `roleIds` taşır, ikinci bir `microservice_get` gerekmez.
|
|
659
|
+
- **Rol tek başına kapı değildir** — §"Uçlar varsayılan olarak ANONİMDİR": koruma `middlewareIds`
|
|
660
|
+
ile gelir.
|
|
661
|
+
|
|
623
662
|
## Uçlar varsayılan olarak ANONİMDİR
|
|
624
663
|
|
|
625
664
|
Çalıştırma rotası `[AllowAnonymous]`'tur (`MicroservicesController.cs:90`): `middlewareIds` boş
|
|
@@ -629,6 +668,7 @@ Anonim veri toplama (anket, başvuru, geri bildirim) bu sayede kurulabilir — a
|
|
|
629
668
|
|
|
630
669
|
`roles` bir kapı değildir: `roleIds` dolu + `middlewareIds` boş bir uç tokensız POST'u 200 ile
|
|
631
670
|
kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware')` Tarif 3b.
|
|
671
|
+
Rol listesini **değiştirmek** için yukarıdaki §"Patch belgesi OKUMA şeklidir".
|
|
632
672
|
|
|
633
673
|
> **Anonimlik/erişim testi `microservice_execute` + `anonymous: true` ile yapılır.** Bayrak
|
|
634
674
|
> verildiğinde gateway isteğe HİÇBİR `Authorization` header'ı koymaz (varsayılan davranışta
|
|
@@ -12,6 +12,10 @@ Gerçek projelerde tek tanım 20-100 bin satır JSON olabilir. **Tam gövdeyi ba
|
|
|
12
12
|
belleğinde çeker, patch'i uygular, politika
|
|
13
13
|
kontrolünden geçirir, geri yazar
|
|
14
14
|
5. *_get view:'outline' → değişikliği teyit et
|
|
15
|
+
|
|
16
|
+
function_update {id, body} → FONKSİYONDA *_patch YOKTUR: tek büyük alan
|
|
17
|
+
gövdedir, yalnız onu gönder (gateway kalan
|
|
18
|
+
alanları mevcut kayıttan doldurur)
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
`view:'full'` **son çaredir** — yalnız küçük kayıtlarda ya da patch'in kurulamadığı köklü
|
|
@@ -23,12 +27,19 @@ yeniden yazımlarda.
|
|
|
23
27
|
ekleme), `/fields/5/nullable`.
|
|
24
28
|
- Dizin indekslerini tahmin etme — `view:'outline'` çıktısındaki sıradan ya da
|
|
25
29
|
`view:'part'` ile doğrulayarak yaz.
|
|
30
|
+
- **Bir listeye tek öğe eklemek listeyi yeniden göndermek değildir:** `add` + `/-`
|
|
31
|
+
(`/fields[slug=status]/valueOptions/-`, `/sourceParameters/selectFields/-`); tek öğeyi silmek
|
|
32
|
+
ya da değiştirmek için seçici (`[value=…]`, `[slug=…]`). Enum örnekleri:
|
|
33
|
+
`platform_guide('entity')` §"Tek bir enum değerini ekle / sil / yeniden adlandır".
|
|
26
34
|
- Silme yerine `replace` tercih et; `remove` kullanacaksan önce o path'i `part` ile görüntüle.
|
|
27
35
|
- Yanıttaki outline özeti değişikliğin uygulandığı hâli gösterir — teyit için oku.
|
|
28
36
|
- **Kullanıcı fonksiyonunda patch aracı yok:** `function_update` tam gövde yazar ama vermediğin
|
|
29
37
|
alanları mevcut kayıttan doldurur — yalnız `body` göndermen yeter (fonksiyonun tek büyük alanı
|
|
30
|
-
gövdedir).
|
|
31
|
-
|
|
38
|
+
gövdedir). `function_get` çıktısı `function_update`'in şablonudur; örnek:
|
|
39
|
+
`ngapps://examples/function-total` `functionUpdateArgs`. Adı değiştirmeden önce
|
|
40
|
+
`dependency_graph` (`module:'ui-function'`) ile çağıranları bul: `{{ }}`/`code` ifadelerindeki
|
|
41
|
+
`Functions.<eskiAd>(` çağrıları metinseldir — platform onları aynı işlemde taşır, ayrıntı
|
|
42
|
+
`platform_guide('functions')` §Güncelleme ve silme.
|
|
32
43
|
|
|
33
44
|
## Değiştirmeden önce geçmişe bak
|
|
34
45
|
|