@appaflytech/zeyrek-ai-mcp 0.0.4 → 0.0.6
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/.env.example +8 -3
- package/.env.production.example +2 -0
- package/README.md +229 -193
- package/dist/config.js +20 -2
- package/dist/knowledge/backendConstraints.js +897 -0
- package/dist/knowledge/componentCatalog.js +196 -42
- package/dist/knowledge/coverage.js +43 -0
- package/dist/knowledge/flowGuards.js +30 -0
- package/dist/knowledge/guideSections.js +49 -0
- package/dist/knowledge/hydrate.js +97 -0
- package/dist/knowledge/languageCodes.js +36 -0
- package/dist/knowledge/languages.js +105 -0
- package/dist/knowledge/layoutSummary.js +10 -0
- package/dist/knowledge/localization.js +51 -0
- package/dist/knowledge/normalize.js +449 -16
- package/dist/knowledge/outlines.js +136 -12
- package/dist/knowledge/propTypes.js +80 -0
- package/dist/knowledge/rules.js +618 -43
- package/dist/knowledge/schemaGuard.js +199 -0
- package/dist/knowledge/styleResolve.js +63 -4
- package/dist/observability/trace.js +27 -3
- package/dist/platform/errors.js +46 -4
- package/dist/platform/http.js +33 -4
- package/dist/platform/patch.js +27 -0
- package/dist/platform/refs.js +104 -0
- package/dist/platform/responses.js +9 -2
- package/dist/platform/screenshot.js +527 -16
- package/dist/platform/secrets.js +40 -0
- package/dist/platform/tokenVault.js +89 -0
- package/dist/platform/writeDiff.js +281 -0
- package/dist/resources/examples/layout-app-shell.json +1 -1
- package/dist/resources/examples/microservice-table-list.json +2 -4
- package/dist/resources/examples/page-dynamic-form.json +2 -2
- package/dist/resources/examples/page-flex-sections.json +99 -0
- package/dist/resources/examples/page-sign-in.json +3 -2
- package/dist/resources/guides/component.md +69 -8
- package/dist/resources/guides/entity.md +404 -16
- package/dist/resources/guides/expressions.md +44 -2
- package/dist/resources/guides/external-component.md +9 -0
- package/dist/resources/guides/getting-started.md +77 -7
- package/dist/resources/guides/layout.md +53 -10
- package/dist/resources/guides/microservice.md +226 -13
- package/dist/resources/guides/middleware.md +89 -1
- package/dist/resources/guides/page.md +383 -46
- package/dist/resources/guides/translation.md +126 -8
- package/dist/resources/guides/view.md +16 -1
- package/dist/resources/guides/workflow.md +198 -27
- package/dist/resources/knowledge.js +5 -0
- package/dist/resources/rules.js +27 -0
- package/dist/resources/schema-fingerprint.json +8 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +33 -0
- package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
- package/dist/resources/schemas/CreateEntityCommand.json +52 -12
- package/dist/resources/schemas/CreateMicroserviceCommand.json +26 -2
- package/dist/resources/schemas/CreateQueryCommand.json +56 -3
- package/dist/resources/schemas/CreateRowCommand.json +38 -0
- package/dist/resources/schemas/CreateViewCommand.json +13 -10
- package/dist/resources/schemas/CreateWorkflowCommand.json +95 -14
- package/dist/resources/schemas/GetRowsQuery.json +3 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +16 -0
- package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
- package/dist/resources/schemas/PreviewWorkflowCommand.json +34 -9
- package/dist/resources/schemas/StepDto.json +23 -3
- package/dist/resources/schemas/UpdateBulkRowCommand.json +33 -0
- package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/UpdateEntityCommand.json +49 -10
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +28 -3
- package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
- package/dist/resources/schemas/UpdateRowCommand.json +40 -0
- package/dist/resources/schemas/UpdateViewCommand.json +15 -10
- package/dist/resources/schemas/UpdateWorkflowCommand.json +95 -14
- package/dist/server.js +4 -0
- package/dist/tools/access.js +3 -3
- package/dist/tools/applications.js +1 -1
- package/dist/tools/automations.js +63 -30
- package/dist/tools/common.js +39 -0
- package/dist/tools/components.js +16 -7
- package/dist/tools/datasources.js +83 -21
- package/dist/tools/deployments.js +34 -2
- package/dist/tools/dtos.js +25 -12
- package/dist/tools/entities.js +113 -28
- package/dist/tools/externalComponents.js +9 -3
- package/dist/tools/files.js +20 -6
- package/dist/tools/flows.js +110 -11
- package/dist/tools/folders.js +7 -2
- package/dist/tools/globalFunctions.js +63 -0
- package/dist/tools/layouts.js +103 -29
- package/dist/tools/microservices.js +229 -38
- package/dist/tools/organizations.js +5 -6
- package/dist/tools/pages.js +365 -100
- package/dist/tools/platformInfo.js +152 -11
- package/dist/tools/projects.js +183 -33
- package/dist/tools/queries.js +32 -10
- package/dist/tools/rows.js +159 -45
- package/dist/tools/screenWrite.js +59 -0
- package/dist/tools/screens.js +28 -0
- package/dist/tools/translations.js +24 -8
- package/dist/tools/views.js +20 -10
- package/dist/tools/workflows.js +33 -16
- package/package.json +6 -3
|
@@ -15,7 +15,10 @@ Uydurma fonksiyon adı runtime'da `X is not defined` 500'üdür — yalnız bura
|
|
|
15
15
|
**Değişkenler:** step çıktıları (`<stepAdı>.data`), middleware değişkenleri (`user`,
|
|
16
16
|
`settings`, `permissions`… — `workflow_middleware_variables` ile keşfet), HTTP bağlamı:
|
|
17
17
|
`cookie`, `header`, `queryString`, `body`, `triggerSource`/`context` (tetikleyen ucun
|
|
18
|
-
type/name/roles/impersonatedUserId'ı).
|
|
18
|
+
type/name/roles/impersonatedUserId'ı). Gövde bir JSON **nesnesi** ise `body.alan` diye okunur,
|
|
19
|
+
gövde bir JSON **dizisi** ise `body[0].alan` diye — 2026-09-04 öncesinde nesne gövde de diziye
|
|
20
|
+
sarılıyordu ve `body.alan` sessizce `undefined` dönüyordu, bu yüzden gövdeye bakan yetki kapıları
|
|
21
|
+
açık kalıyordu. Workflow'da istek girdisi `input.`, automation'da
|
|
19
22
|
tetikleyen satır `row` / `previousRow` / `user`.
|
|
20
23
|
|
|
21
24
|
**Fonksiyonlar (qm'de aktif kullanılanlar önce):**
|
|
@@ -57,6 +60,27 @@ tetikleyen satır `row` / `previousRow` / `user`.
|
|
|
57
60
|
(motor tr-TR + UTC); automation zinciri istek başına workflow başına 10 koşumda
|
|
58
61
|
`InfiniteLoopException` ile kesilir (sonsuz döngü koruması).
|
|
59
62
|
|
|
63
|
+
> **İki ayrı sınır var ve önce KÜÇÜK olan konuşur.** `AutomationConsumer` automation'ı
|
|
64
|
+
> çalıştırmadan önce `triggerCount > 2` kontrolü yapar (yani 3. tetiklemede keser);
|
|
65
|
+
> `WorkflowContext.CheckProcessCount` ise workflow koşumunu 10'da keser. Yukarıdaki 10 yalnız
|
|
66
|
+
> ikincisidir. Hata mesajı artık hangi automation'ın kaç kez tetiklendiğini ve sınırı söyler.
|
|
67
|
+
>
|
|
68
|
+
> **Kapıyı doğru yere koy:** sayaç, akışın ilk adımı koşmadan önce artar. Tetikleyen satıra geri
|
|
69
|
+
> yazan bir automation'da `row.status !== previousRow.status` kapısını akışın içindeki bir
|
|
70
|
+
> `condition` ADIMINA koymak koşumu saydırmayı sürdürür; aynı ifade `automation.condition`
|
|
71
|
+
> alanında ise yeniden tetikleme hiç doğmaz ve sayaç artmaz (`RowConsumer.cs:130-143`).
|
|
72
|
+
>
|
|
73
|
+
> **Yanıltıcı `failed` DÜZELTİLDİ (2026-09-05).** Sayaç pas başına İKİ artıyordu: runner'ın kendi
|
|
74
|
+
> 10'luk koruması (`CheckProcessCount`) ile tetikleme zincirinin sayacı aynı sözlüğü paylaşıyordu,
|
|
75
|
+
> bu yüzden tetikleyen satırını güncelleyen her automation ikinci pasında `processCount: 3` ile
|
|
76
|
+
> kesiliyordu — ölçümde 10 durum değişikliğinin **10'unda da**. İki sayaç ayrıldı; artık her pas
|
|
77
|
+
> zinciri bir artırır: 1. pas kapıyı geçip yazar, 2. pas kapıya takılıp durur ve zincir `2`'de
|
|
78
|
+
> biter. Ölçüm (aralıklı 10 güncelleme): 20 olay, hepsi `completed`, `failed` yok.
|
|
79
|
+
>
|
|
80
|
+
> Kalan tek durum bir YARIŞTIR: aynı satıra ara vermeden art arda güncelleme atılırsa bir zincir
|
|
81
|
+
> önceki zincirin yazması havadayken başlayabilir ve seyrek bir `failed` düşebilir (10 hızlı
|
|
82
|
+
> güncellemede 1). Satır başına tek akış bekliyorsanız güncellemeleri aralıklandırın.
|
|
83
|
+
|
|
60
84
|
**Tohum parola üretmek istiyorsan** hash'i platformun kendisine ürettir: parolayı düz metin alan
|
|
61
85
|
bir workflow yazıp `crypto.SHA256.encode(input.password)` sonucunu döndür, ya da kullanıcı kaydını
|
|
62
86
|
açan ucu kullan. Dışarıda kendi SHA-256'nı hesaplayıp yazarsan giriş akışı **hiçbir zaman**
|
|
@@ -73,14 +97,32 @@ dalından devam eder.
|
|
|
73
97
|
| `Page` | `Page.user`, `Page.variables`, `Page.components`, `Page.properties`, `Page.queryParams`, `Page.authorized(roles)` |
|
|
74
98
|
| **Komponent adları** | Her komponent adıyla doğrudan değişkendir: `specialties_table.rows = data` hem prop'u yazar hem UI'ı günceller (change event). Addaki `-` silinir: `tickets-table` → `ticketstable` |
|
|
75
99
|
| `state.page` / `state.app` | Kalıcı state (oku/yaz): `state.page.selectedId = row.id`. **Tanımlı değişkenler de buradadır:** `variable_create` ile yazılan her `key` sayfa düzeyinde `state.page.<key>`, uygulama düzeyinde `state.app.<key>` olur; `value` nesneyse alanlarına inilir (`state.page.MyTest.isActive`) |
|
|
76
|
-
| `Steps` | Adım çıktıları: `Steps.load_specialties`
|
|
100
|
+
| `Steps` | Adım çıktıları: `Steps.load_specialties` — anahtar adımın **ADIdır**, `stepId` değil (`setStorage(task.name, …)`); `Steps.<adım>.error` o adımın hatası — ama YALNIZ HTTP durum metnidir (401 için `'Unauthorized'`), ucun mesajı değil. `Steps.error` ise hata akışı için doldurulur (`onError` koşmadan önce `setStorage('error', …)` yapar) ama **YAPIŞKANDIR**: storage sayfa başına tek `FlowMachine`'de yaşar ve `'error'` anahtarı hiç temizlenmez, yani ilk hatadan sonra o sayfadaki her akışta dolu kalır — normal akışta `Steps.error` okuyan bir ifade bayat hatayı görür ve şekli `{ message, code, step: { id, name, type, data } }`'dir: kullanıcıya gösterilecek metin `Steps.error.message`'tır (`err.response.data.message`, boşsa durum metni). **Başarılı bir adımda `Steps.<adım>.data` gerçek bir `Array`'dir** — `.map` · `.filter` · `.concat` · `.length` çalışır. Adım HATA dönerse aynı alan ucun hata gövdesini taşır; aşağıdaki kutuya bakın |
|
|
77
101
|
| `self` · `item` · `Row` / `RowIndex` | Tetikleyen komponent (yazılabilir) · repeater/tablo satırı (yazmak satırı günceller) |
|
|
78
102
|
| `event` | `{name, data, source: {id, props}}` |
|
|
79
103
|
| `cookie` | `cookie.get(name)` / `cookie.set(name, value, {expiresInHours, path})` / `cookie.remove(name)` — tarayıcı cookie'si |
|
|
80
104
|
| `setComponentProperty(name, key, value)` / `getComponentProperty(name, key)` | fonksiyon alternatifi |
|
|
81
105
|
| `LocalStorage` · `Widget` · `params` | yerel veri · widget bağlamı · olay parametreleri |
|
|
106
|
+
| `t(key)` | Çeviri anahtarını çözer: aktif dil → projenin varsayılan dili → anahtarın kendisi. Boş string de "eksik" sayılır. Sözlük `machine.context.state.translations`'tan gelir; dil `language-selector` ile değişince bir sonraki çağrı yeni dili görür. **Yalnız UI flow kodunda vardır** — backend ifadelerinde karşılığı `Loc()`'tur (`platform_guide('workflow')`) | `t('home.welcome')` |
|
|
82
107
|
| `log(...)` | console (proxy'leri temizler) |
|
|
83
108
|
|
|
109
|
+
> **`.map is not a function` bir dizi sözleşmesi sorunu DEĞİL, düşmüş bir adımdır.**
|
|
110
|
+
> Ölçüldü 2026-09-05 (`f09-repro/probe`, yerel kurulum): başarılı bir microservice adımında
|
|
111
|
+
> `Steps.<adım>.data` `[object Array]`'dir, `Array.isArray` true döner ve `.map`/`.filter`/
|
|
112
|
+
> `.concat` çalışır. Adım hata döndüğünde (401, 500, geçersiz parametre) motor aynı alana ucun
|
|
113
|
+
> **hata gövdesini** yazar — o bir nesnedir, dizi metodları yoktur ve
|
|
114
|
+
> `(Steps.x.data || []).map is not a function` tam olarak burada çıkar. 2026-09-05 koşusunda
|
|
115
|
+
> boş kalan şirket filtresinin sebebi buydu: adım korumalı bir uca 401 ile düşmüştü.
|
|
116
|
+
> **Teşhis sırası:** önce `Steps.<adım>.error`'a bak, sonra ifadeyi suçla. Bir dizi bekliyorsan
|
|
117
|
+
> `Array.isArray(Steps.<adım>.data)` ile koru; boş listeye düşmek, hata gövdesi üzerinde
|
|
118
|
+
> dönmekten iyidir.
|
|
119
|
+
>
|
|
120
|
+
> **Patlayan ifade artık sessiz değil (F-09).** Hedef prop yine YAZILMAZ — bozuk bir değeri
|
|
121
|
+
> yazmak durumu kötüleştirir — ama akış bunu yapısal olarak bildirir: `page_screenshot` yanıtı
|
|
122
|
+
> `runtimeErrors` taşır ve hangi komponentin hangi prop'unun yazılamadığını (`element` ·
|
|
123
|
+
> `property`) söyler. Aynı defter hata dönen microservice adımlarını da kaydeder. **Kare
|
|
124
|
+
> "çalışıyor" görünse bile `runtimeErrors` doluysa o adımlar işini YAPMAMIŞTIR.**
|
|
125
|
+
|
|
84
126
|
**Prop'a doğrudan ifade bağlama (`{{ }}`):** akış kurmadan bir prop'u ifadeye bağlamak için
|
|
85
127
|
prop `type: 'code'` olur, **ifade `ref` alanına `{{ }}` içinde** yazılır, `data` tasarım zamanı
|
|
86
128
|
değerini taşır:
|
|
@@ -127,3 +127,12 @@ Platform, npm paketi olmadan servis eder:
|
|
|
127
127
|
|
|
128
128
|
`external_component_scaffold` bu sözleşmeye uyan bir iskeleti yerel dizine
|
|
129
129
|
zaten yazar; kit, iskeleti elle düzenlerken referans olarak işinize yarar.
|
|
130
|
+
|
|
131
|
+
## Backend doğrulama sınırları (EXT-001)
|
|
132
|
+
|
|
133
|
+
`external_component_publish` manifest'i kayıt ucuna gitmeden denetler (block): `protocolVersion`
|
|
134
|
+
1; `definition.blocks` en az bir blok; `definition.props[].name` DOM/host adı olamaz
|
|
135
|
+
(`attributes`, `children`, `class`, `className`, `id`, `name`, `style`, `slot`, `innerHTML`…) ve
|
|
136
|
+
`zx`/`on` ile başlayamaz; `definition.props[].type` platform sözlüğünden (`text`, `number`,
|
|
137
|
+
`switch`, `dropdown`, `object`, `array`, `views`, `component`…). Slug deseni `^x-[a-z][a-z0-9-]*$`
|
|
138
|
+
ve 2 MB paket sınırı tool'un kendisinde denetlenir. Tam liste: `ngapps://rules/backend-constraints`.
|
|
@@ -7,21 +7,41 @@ Bağımlılıklar bu sırayı zorunlu kılar; atlama:
|
|
|
7
7
|
```
|
|
8
8
|
1. project_create (application zorunlu: ilk application projeyle birlikte açılır;
|
|
9
9
|
application slug'ı ORG GENELİNDE tekildir — 'web' büyük olasılıkla
|
|
10
|
-
doludur, 409'da project_list ile mevcut slug'ları tara
|
|
10
|
+
doludur, 409'da project_list ile mevcut slug'ları tara.
|
|
11
|
+
DİL: gateway create'ten sonra projenin dillerini kurar — verirsen
|
|
12
|
+
languages/defaultLanguage'ı, vermezsen tek dillik ['en'] tohumunu
|
|
13
|
+
(PRJ-003). Ürünün dili farklıysa BURADA yaz; dilsiz projede yazılan
|
|
14
|
+
çeviriler runtime'da ham anahtar olarak çizilir, bkz.
|
|
15
|
+
platform_guide('translation'))
|
|
11
16
|
2. project_get (environment id'leri + application id burada)
|
|
12
|
-
3. datasource_list (
|
|
13
|
-
|
|
17
|
+
3. datasource_list (built-in datasource PROJEYLE BİRLİKTE doğar: project_create ayrı bir
|
|
18
|
+
veritabanı, her environment için ayrı bir şema ve o veritabanına bağlı
|
|
19
|
+
built-in datasource açar. Entity'lerini oraya aç — listede
|
|
20
|
+
isBuiltIn: true olan kayıt odur, silinemez.
|
|
21
|
+
TEMİZ BİR İSİM ALANI İSTİYORSAN YENİ PROJE AÇ, yeni datasource değil:
|
|
22
|
+
tek datasource içinde önek/şema ayırmanın yolu YOK.
|
|
23
|
+
datasource_create YALNIZ kullanıcı kendi veritabanını verdiğinde
|
|
24
|
+
çağrılır ('host/port/kullanıcı/parola şunlar'); built-in'in parolası
|
|
25
|
+
hiçbir tool'dan okunamaz ve okunması gerekmez. Dış bir DB'ye kurulan
|
|
26
|
+
entity users'a ilişki KURAMAZ — kimlik gerektiren her uygulama
|
|
27
|
+
built-in'de kurulur, bkz. platform_guide('entity'))
|
|
14
28
|
4. entity_list (ÖNCE BAK: platform her projeye hazır entity'ler açar — users (kimlik:
|
|
15
29
|
(applicationId'siz) email/password/first_name/last_name), roles, user_roles, permissions.
|
|
16
30
|
applicationId VERİRSEN bunlar listede GÖRÜNMEZ. Ürünün "müşteri" /
|
|
17
31
|
"öğrenci" / "üye" kavramı için sıfırdan bir kimlik tablosu açmadan önce
|
|
18
|
-
users'ı gör — bkz. platform_guide('entity') §'Hazır entity'ler'
|
|
32
|
+
users'ı gör — bkz. platform_guide('entity') §'Hazır entity'ler'.
|
|
33
|
+
ROL: role_create diye bir tool YOK ve olmamalı — roles sıradan bir
|
|
34
|
+
entity'dir. Projeyle gelen roller yetmiyorsa yol entity_get('roles') +
|
|
35
|
+
kendi microservice ucun; sayfaya bağlama page_create roleIds ile)
|
|
19
36
|
5. entity_create (applicationIds Studio'nun application bazlı entity listesi içindir —
|
|
20
37
|
boşsa satır uçları ÇALIŞIR ama entity Studio listesinde görünmez;
|
|
21
38
|
önce ilişkisiz gövdeler, sonra update/patch ile ilişki alanları —
|
|
22
39
|
bkz. platform_guide('entity'))
|
|
23
40
|
6. row_create (test verisi; alan tanımlarını row_list ile doğrula)
|
|
24
|
-
7. dto_list / dto_create
|
|
41
|
+
7. dto_list / dto_create — çocuk alanın `type` değeri **`"string"`**'tir; `"text"` yazmak sessizce
|
|
42
|
+
`object`'e çevrilir ve DTO bozuk kurulur (ölçüldü 2026-09-05, teşhis platformun kendi ürettiği
|
|
43
|
+
bir DTO'yu okuyarak yapıldı). Şablon için var olan bir DTO'yu `dto_get` ile alın.
|
|
44
|
+
(workflow/automation'ın parameterDtoId/returnDtoId sözleşmeleri;
|
|
25
45
|
platform entity başına DTO üretir — önce dto_list ile ara)
|
|
26
46
|
8. workflow_create (type: 'middleware' — YALNIZ ihtiyaç varsa; bkz. aşağıdaki karar tablosu.
|
|
27
47
|
Kuruyorsan guard + 'user' değişkeni; uçlara middlewareIds ile
|
|
@@ -51,8 +71,17 @@ kilitler:
|
|
|
51
71
|
2. O workflow'un ucu, kimlik kurulduğu için `middlewareIds` ile auth middleware'ine bağlıdır.
|
|
52
72
|
3. Auth middleware'i token'ı **çerezten** okur; ortada henüz kullanıcı yoktur, dolayısıyla token
|
|
53
73
|
da yoktur.
|
|
54
|
-
4. **Yeni projede `users` tablosu BOŞ gelir
|
|
55
|
-
otomatik kopyalamaz.
|
|
74
|
+
4. **Yeni projede `users` tablosu BOŞ gelir.** Studio operatörü (`admin@…`) organizasyon
|
|
75
|
+
kullanıcısıdır, uygulamanın kullanıcısı DEĞİLDİR; kimse onu otomatik kopyalamaz.
|
|
76
|
+
|
|
77
|
+
**Bu bir platform boşluğudur, kısa yolu yoktur.** İlk kullanıcıyı açmanın desteklenen tek yolu
|
|
78
|
+
aşağıdaki bootstrap desenidir: geçici bir uç aç, kullanıcıyı yaz, ucu hemen sil. Bakım/migration
|
|
79
|
+
uçlarını (`/organizations/sync-users` gibi `Sync*` uçları) **kullanma** — onlar ürün yüzeyinin
|
|
80
|
+
parçası değildir, özellik geliştirirken veri düzeltmek için yazılmışlardır ve kaldırılmaktadırlar.
|
|
81
|
+
Gateway de onları araç olarak yayınlamaz.
|
|
82
|
+
|
|
83
|
+
Bu tur, kimlik kuran her yeni projede ödenmek zorunda: iki koşuda da (09-02 ve 09-04) `users`
|
|
84
|
+
boş bulundu ve ikisi de bu deseni elle kurdu.
|
|
56
85
|
|
|
57
86
|
**Bootstrap deseni** (crm koşumunda uygulandı):
|
|
58
87
|
|
|
@@ -107,3 +136,44 @@ koda baktığını gizlemek yerine raporlamak.
|
|
|
107
136
|
- microservice → `microservice_execute`, beklenmedik sonuçta `microservice_debug`
|
|
108
137
|
- workflow → `workflow_preview`; automation → `automation_trigger` + `automation_histories`
|
|
109
138
|
- "API 200 döndü" tek başına bitti saymaz; politika bulgularını (`policyFindings`) oku ve gereğini yap
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## Yazdıktan sonra `silentlyDropped` alanına bak
|
|
142
|
+
|
|
143
|
+
Platformun bir alanı **hatasız yutması** bu depodaki en pahalı hata sınıfı: çağrı 200 döner,
|
|
144
|
+
`appliedPatch` uygulandığını söyler, `*_get` değeri geri verir ve o alan hiç işe yaramaz.
|
|
145
|
+
2026-09-05 koşusunda kaybedilen sürenin çoğu sekiz böyle çağrıdan geldi.
|
|
146
|
+
|
|
147
|
+
Gateway yazma tool'larında gönderdiğin gövdeyi kaydedilenle karşılaştırır ve farkı yanıta
|
|
148
|
+
`silentlyDropped` olarak koyar (`microservice_*`, `query_*`, `flow_*`, `dto_*`, `workflow_*`,
|
|
149
|
+
`layout_create`, `page_schema_patch`). Boş değilse o alan **kaydedilmemiştir**:
|
|
150
|
+
|
|
151
|
+
```jsonc
|
|
152
|
+
"silentlyDropped": [
|
|
153
|
+
"steps[stepId=sync_filters].args.expression: gönderilen \"state.page.month = …\" ↔ kaydedilen yok"
|
|
154
|
+
]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Diff dizi öğelerini indeksle değil KİMLİKLE eşler (`stepId`, `elementId`, `name`, `id`), çünkü
|
|
158
|
+
platform diziyi kendi sırasında geri döndürür. Diff üretilemeyen tek durum, yanıtın kayıt değil
|
|
159
|
+
kabul gövdesi olmasıdır; o zaman alan hiç görünmez ve doğrulamayı `*_get` ile kendin yaparsın.
|
|
160
|
+
|
|
161
|
+
## `unmappedMembers`: anahtarın komutta HİÇ olmadığını söyler
|
|
162
|
+
|
|
163
|
+
İki kanal aynı yanıtta çıkabilir ve **kanıt güçleri farklıdır**:
|
|
164
|
+
|
|
165
|
+
- `silentlyDropped` bir SEZGİSELDİR: gönderdiğin gövdeyi kaydedilenle karşılaştırır. Platform bir
|
|
166
|
+
alanı başka adla saklarsa ya da id'yi yeniden atarsa yanlış alarm verebilir.
|
|
167
|
+
- `unmappedMembers` OTORİTERDİR: o anahtar gönderdiğin komutun şemasında **yoktur**. Platform onu
|
|
168
|
+
hata vermeden yok sayar, yani yazdığını sandığın değer hiç kaydedilmez.
|
|
169
|
+
|
|
170
|
+
```jsonc
|
|
171
|
+
"unmappedMembers": ["indexes[0].isUnique"]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Bu satırı gördüğünde alanı silme — **doğru adı bul**. Şema `ngapps://schemas/<Komut>` altındadır
|
|
175
|
+
(ör. `ngapps://schemas/UpdateEntityCommand`) ve gerçek maliyeti ölçülmüştür: `unique` yerine
|
|
176
|
+
`isUnique` yazılan bir index UNIQUE OLMADAN kurulur ve çağrı 200 döner.
|
|
177
|
+
|
|
178
|
+
Kanal yalnız senin yazdığın anahtarları bildirir; gateway'in bir kaydı okuyup geri yazarken
|
|
179
|
+
taşıdığı alanlar (`slug`, `datasourceId`, `audit` gibi) burada görünmez.
|
|
@@ -12,10 +12,22 @@ kutusu layout modülünde süzülmez — tüm katalog kullanılabilir.
|
|
|
12
12
|
|
|
13
13
|
**2. Şema alanının adı `schema` değil `views`.** (LAY-001)
|
|
14
14
|
|
|
15
|
-
**3. Micro-op
|
|
16
|
-
`PATCH /layouts/.../schema` ucu yok)
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
**3. Micro-op ucu yok, ama `PATCH /layouts/{app}/{id}` var.** `page_schema_patch`'in birebir
|
|
16
|
+
karşılığı yoktur (`PATCH /layouts/.../schema` ucu yok); onun yerine platform genel bir JSON Patch
|
|
17
|
+
ucu verir ve `layout_patch` onu kullanır: layout'u okur, JSON Patch'i ve kendi normalizasyonlarını
|
|
18
|
+
uygular, doğrulamadan geçirir ve **farkı** `{ baseVersion, ops }` olarak yazar — tam gövde PUT'u
|
|
19
|
+
atılmaz. Böylece tek bir başlık düzeltmesi için 12 düğümlük ağacı yeniden göndermezsin (ölçüm:
|
|
20
|
+
09-04 koşusu Y-25). Yol İNDEKS yerine SEÇİCİ alır:
|
|
21
|
+
`/views/0/children[elementId=baslik]/props/content/data`.
|
|
22
|
+
|
|
23
|
+
**Sürüm ve 409.** Okunan kaydın `version` alanı `baseVersion` olarak gider. Sen okuduktan sonra
|
|
24
|
+
başka biri yazdıysa platform **409** döner ve HİÇBİR op uygulanmaz; doğru tepki "aynı op'ları
|
|
25
|
+
tekrar göndermek" değil, `layout_schema_get` ile yeniden okuyup patch'i güncel hâle göre kurmaktır.
|
|
26
|
+
`version` bir **string**tir (mikrosaniye cinsinden zaman damgası, JavaScript'in güvenli tam sayı
|
|
27
|
+
sınırının üstünde) — sayıya çevirme, olduğu gibi geri gönder. `expectedUpdatedAt` verirsen gateway
|
|
28
|
+
ayrıca istek gitmeden önce yerel bir kontrol daha yapar. Tam ağacı kendin yazacaksan `layout_update`;
|
|
29
|
+
`views` göndermezsen mevcut ağaç korunur. Okumak için `layout_schema_get` (summary kipi hedef bulmak
|
|
30
|
+
için yeterlidir).
|
|
19
31
|
|
|
20
32
|
**4. TEK slot.** Sayfa şeması **her** slot'a ayrı ayrı kopyalanır — iki slot, sayfayı iki
|
|
21
33
|
kez çizer ve `elementId`'leri tekrarlar. (LAY-002 / LAY-006)
|
|
@@ -45,7 +57,9 @@ runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
|
|
|
45
57
|
**7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
|
|
46
58
|
Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
|
|
47
59
|
`direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
|
|
48
|
-
alt alta, flex çocukları 49/310px yan yana).
|
|
60
|
+
alt alta, flex çocukları 49/310px yan yana). Not: `wrap` varsayılanı artık açık (2026-09-01) —
|
|
61
|
+
sığmayan çocuk alt satıra SARAR ama bu ızgara yığılması değildir; dar ölçülü çocuklar yan
|
|
62
|
+
yana kalmaya devam eder.
|
|
49
63
|
|
|
50
64
|
**8. Doğrulama tek yerden:** `page_runtime_get`. Layout'un kendi serve URL'i yoktur —
|
|
51
65
|
gerçek çıktıyı, layout'a bağlı bir sayfanın runtime paketinde görürsün (slot yerleşimi
|
|
@@ -67,9 +81,12 @@ container isWrapper:true heightType:'fill' layout:'flex' direction:'row' gap:
|
|
|
67
81
|
└ slot heightType:'fill' block w12 y6
|
|
68
82
|
```
|
|
69
83
|
|
|
70
|
-
- **Genişlik
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
- **Genişlik `appearance.widthType`'tan gelir (2026-09-01); kolon payı fallback'tir.**
|
|
85
|
+
Yan menü için `widthType: 'fixed'` + `widthString: "240px"` (serbest CSS uzunluğu:
|
|
86
|
+
`"50%"`, `"10vw"`, `calc(...)` de geçer; yalnız flex kabın çocuğunda okunur, mutlak
|
|
87
|
+
ızgara kabında okunmaz). `'fill'` kabın tüm genişliğini alır, `'auto'` içeriğe oturur;
|
|
88
|
+
`widthType` HİÇ yazılmazsa `w:2` = 12'de 2 → 1440px tasarım genişliğinde 240px payı
|
|
89
|
+
geçerlidir. String yalnız `'fixed'` modda okunur — tek başına `widthString` ölüdür.
|
|
73
90
|
- **Yükseklik `fill`'dir**, `size.h` değil: kök ekranı doldurur, yan menü ve içerik sütunu
|
|
74
91
|
kökün bıraktığını, slot da üst şeritten artanı alır. Slot'un grid satırı ayrıca açık
|
|
75
92
|
uçludur (`y / auto`), yani sayfa uzadıkça slot uzar.
|
|
@@ -115,6 +132,29 @@ bağlantı nesnesi ya da tam yol).
|
|
|
115
132
|
// flex anlamının devreye girmemesi.
|
|
116
133
|
```
|
|
117
134
|
|
|
135
|
+
## Çeviri: header/sidebar etiketini anahtara bağlamak
|
|
136
|
+
|
|
137
|
+
Layout'un `views` ağacındaki bir etiketi çeviriye bağlamak için **bütün ağacı yeniden yazma**.
|
|
138
|
+
`layout_patch` `set_prop_type` micro-op'unu tanır ve prop'un tipini yerinde değiştirir:
|
|
139
|
+
|
|
140
|
+
```jsonc
|
|
141
|
+
{ "op": "set_prop_type", "target": "app_header_title", "prop": "content",
|
|
142
|
+
"propType": "translation", "ref": "layout.header.title" }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- Anahtar **`ref`'te** yaşar, `data`'da değil — runtime `data`'yı her turda çözümlenmiş değerle
|
|
146
|
+
ezer. `ref` boşsa gateway isteği göndermeden reddeder.
|
|
147
|
+
- Mevcut statik metin `data`'da yedek olarak KALIR, böylece Studio canvas'ında boş kutu görünmez.
|
|
148
|
+
- `set_prop_type` ile JSON Patch aynı `ops` listesinde karışık verilebilir; micro-op'lar önce
|
|
149
|
+
uygulanır, sonra patch yolları çözülür.
|
|
150
|
+
- Aynı op sayfa tarafında `page_schema_patch` ile çalışır — sözleşme birebir aynıdır. Anahtarları
|
|
151
|
+
`translation_list` ile bulun; fallback zinciri ve `t:` doğrulama mesajı sözleşmesi
|
|
152
|
+
`platform_guide('translation')`'da.
|
|
153
|
+
|
|
154
|
+
> Layout'un diğer micro-op'ları YOKTUR: `add_component`, `remove_component`, `replace_prop` ve
|
|
155
|
+
> arkadaşları yalnız sayfa hattındadır. Layout'ta düğüm eklemek/silmek için JSON Patch ya da
|
|
156
|
+
> `layout_update` kullanılır.
|
|
157
|
+
|
|
118
158
|
## Sayfayı layout'a bağlama
|
|
119
159
|
|
|
120
160
|
`page_update` ile `layoutId`. Bağlı sayfanın kendi şeması slot'un içine girer; sayfanın
|
|
@@ -140,8 +180,11 @@ bunlardan biri olabilir:
|
|
|
140
180
|
(`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
|
|
141
181
|
(`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
|
|
142
182
|
`packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
|
|
143
|
-
`packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `
|
|
144
|
-
|
|
183
|
+
`packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthType`
|
|
184
|
+
fill/fixed/auto dalları, `normalizeCssSize` kabul kuralı; tuval ve runtime aynı fonksiyonu okur),
|
|
185
|
+
`apps/studio/src/features/page/components/appearance/appearance-size.functions.ts` (panelin
|
|
186
|
+
mod↔string yazım kuralları), `apps/studio/src/components/screen-builder/quick-stack.functions.ts`
|
|
187
|
+
(Quick Stack iskeleti; `docs/features/quick-stack.md`),
|
|
145
188
|
`apps/studio/src/features/layout/layout.functions.ts` (`getDefaultLayout` — Kural 5a/5b'deki iskelet),
|
|
146
189
|
`docs/components/layout-composition.md`, `docs/specs/2026-08-28-free-layout-responsive-design.md`,
|
|
147
190
|
`docs/issues.md` ARC-031 / BE-002 / CQ-143.
|
|
@@ -43,23 +43,119 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
|
|
|
43
43
|
(varsayılan 20) ve `offset` alır. Kurulum sırası: `view_create` (aranacak tablo/alanlar +
|
|
44
44
|
analizör) → `view_rebuild` → `source:'view'` microservice. Satış özeti / top-N gibi
|
|
45
45
|
rapor işleri view'e DEĞİL raw query'e aittir (`query_create` + query kaynaklı uç).
|
|
46
|
+
**Alan kapsamı:** view kaynaklı uç `selectFields` boşken yalnız view'ın indekslediği
|
|
47
|
+
alanları + birincil anahtarı döndürür. İndekslenmemiş kolon (maaş, TCKN) kendiliğinden
|
|
48
|
+
gelmez; daha fazlasını istiyorsan `selectFields` ile açıkça iste (İK koşusu M-7).
|
|
46
49
|
- `workflow`: çok adımlı iş mantığı gerekiyorsa microservice'i workflow'a bağla
|
|
47
|
-
(`workflowId`).
|
|
50
|
+
(`workflowId`). **Yanıtı açık bir DTO sözleşmesine bağlamanın tek yolu da budur** — DTO
|
|
51
|
+
alanları uçta değil akıştadır; bkz. aşağıda §"Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a
|
|
52
|
+
bağlanır".
|
|
48
53
|
- `query`: yalnız ham SQL gerçekten şartsa. Politika lint'i her query kullanımında gerekçe
|
|
49
54
|
ister (MS-002, warn).
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
|
|
56
|
+
> **BOŞ STRING PARAMETRE `NULL`'A ÇEVRİLİR — sorgu sessizce 0 satır döner.** Platform
|
|
57
|
+
> önizleme/çalıştırma parametrelerini normalize ederken boş ya da yalnız boşluktan oluşan
|
|
58
|
+
> değeri `null` yapar. `WHERE company_id = @company` yazıp `company: ''` gönderirsen koşul
|
|
59
|
+
> `= NULL` olur ve **hiçbir satırla eşleşmez**: HTTP 200, boş sonuç, hata yok, uyarı yok.
|
|
60
|
+
> Üç koşuda üst üste ölçüldü (09-02, 09-04 birinci ve dördüncü koşu) ve üçünde de teşhis
|
|
61
|
+
> ancak `SELECT (@p IS NULL)` gibi bir sonda ile yapılabildi.
|
|
62
|
+
>
|
|
63
|
+
> **Parametre listesi ZORUNLUDUR.** `@token` kullanan bir sorguyu `query_create` ile
|
|
64
|
+
> kaydederken her belirteci `parameters` içinde `{ name, type, value }` olarak bildir. Eksik
|
|
65
|
+
> bırakılırsa istek **400** döner ve mesaj hangi belirtecin bildirilmediğini adıyla söyler.
|
|
66
|
+
> (2026-09-04'ten önce aynı durum `500 "Please correct the query syntax."` veriyordu ve SQL'i
|
|
67
|
+
> suçluyordu; kaydetme yolu gövdeyi önce bir kez çalıştırıp yanıt şemasını ürettiği için
|
|
68
|
+
> bağlanmamış belirteç sözdizimi hatası gibi görünüyordu.) `value` yalnız kayıt anında
|
|
69
|
+
> kullanılır: SQL doğrulanır ve `returnScheme` ondan üretilir.
|
|
70
|
+
> Yorum satırındaki `{{ad}}`/`@ad` belirteçleri parametre sayılmaz — kaydetme yolu
|
|
71
|
+
> sorguyu yorumlarından arındırdıktan sonra tarar. SQL çalıştırılamazsa kayıt **yine de**
|
|
72
|
+
> oluşur: `returnScheme` boş kalır ve yanıttaki `message` nedeni söyler; SQL'i düzeltip
|
|
73
|
+
> `query_update` ile kaydet.
|
|
74
|
+
>
|
|
75
|
+
> "Filtre yok" demek istiyorsan iki doğru kalıptan birini kullan:
|
|
76
|
+
> `(@company IS NULL OR c.company_id::text = @company)` ya da bir sentinel değer
|
|
77
|
+
> (`'all'`) ve SQL'de ona bakan bir dal. Aynı davranış workflow query adımında da geçerli;
|
|
78
|
+
> oradaki anlatım `platform_guide('workflow')` "Query adımı" bölümünde.
|
|
79
|
+
>
|
|
80
|
+
> **KAPSAM UYARISI: query kaynaklı ucun parametreleri ÇAĞRIDAN gelir.** Middleware'in
|
|
81
|
+
> kurduğu `user` bu yola girmez — kimlik/tenant kapsamını gövdeye yazan bir uç
|
|
82
|
+
> sahtelenebilir (ölçüldü 2026-09-07). Kapsam sunucuda zorlanacaksa sorguyu bir workflow
|
|
83
|
+
> query adımından çağır; reçete aşağıda §"Kimlik kapsaması × uç kaynağı".
|
|
84
|
+
2. **`selectFields` asla boş bırakılmaz.** Boş = platform TÜM alanları döndürür (view kaynaklı
|
|
85
|
+
uçta: yalnız view'ın indekslediği alanlar + birincil anahtar); join'li sorguda maliyet
|
|
86
|
+
katlanır. Join varken boş bırakmak block'tur (MS-001).
|
|
52
87
|
3. **Join'ler ilişkilerden türetilir.** `sourceParameters.joins` öğesi:
|
|
53
88
|
`{ alias, entityId, fieldId, path, joins }` — iç içe join mümkündür. Her join'in getirdiği
|
|
54
89
|
alanlar da `selectFields`'ta `{ alias, fieldId, path, fieldSlug }` ile açıkça seçilir.
|
|
55
90
|
**`path` boş bırakılamaz** — Studio join'li seçimleri yalnız `path` üzerinden eşleştirir
|
|
56
91
|
(aşağıdaki bölüme bak).
|
|
92
|
+
3b. **Kayıtlı ucu önizleme:** `microservice_preview` tam gövde istemez — `id` verirsen gateway
|
|
93
|
+
tanımı platformdan çeker ve yalnız `previewParameters`'ı senden alır (09-04 Y-09). Şekil
|
|
94
|
+
`{ body: [{…}] }` · `{ queryString: {…} }` · `{ routeParams: {…} }`; **tanımsız bir şekil
|
|
95
|
+
sessizce yutulur** ve bütün sayaçlar 0 döner (09-02 M-2).
|
|
57
96
|
4. **HTTP sözleşmesi:** `type: 'rest-api'` + `httpMethod: get|post|put|delete` + `url`
|
|
58
97
|
(route şablonu). Zamanlanmış işler `type: 'schedule-job'`, kuyruk tüketicileri `consumer`.
|
|
98
|
+
4b. **`query_preview` yazma sorgusunu ÇALIŞTIRIR, GERİ ALIR ve BUNU SÖYLEMEZ.**
|
|
99
|
+
`INSERT`/`UPDATE`/`DELETE` önizlediğinde `affectedRowCount` **gerçek** sayıyı döndürür ama
|
|
100
|
+
transaction geri alınır ve tabloda satır kalmaz. **Yanıtta rollback işareti yoktur** — yalnız
|
|
101
|
+
`{ affectedRowCount, elapsedMilliseconds }` gelir (2026-09-07'de ölçüldü; `rolledBack` alanı
|
|
102
|
+
yalnız OKUYAN sorgunun yanıt şeklinde var ve orada hep `false`). Yani sayıya bakan çağıran
|
|
103
|
+
yazdığını sanır: 09-04 4. koşusunda 624 satırlık bir seed böyle "yazıldı" sanıldı. Kalıcı
|
|
104
|
+
yazma için sorguyu kaydedip query kaynaklı bir uçtan ya da workflow'dan çalıştır; kalıcılığın
|
|
105
|
+
tek kanıtı sonradan çekilen `row_list`'tir. **Var olan satırlardan türetilmiş satır üretmenin
|
|
106
|
+
uçtan uca reçetesi** (anahtar sözleşmesi dâhil) `platform_guide('entity')` §"Toplu ve
|
|
107
|
+
türetilmiş satır" bölümündedir; aynı davranış `platform_guide('workflow')` "Query adımı"
|
|
108
|
+
bölümünde de yazılıdır.
|
|
109
|
+
|
|
110
|
+
4c. **Sayfalama toplamı `Total-Count` header'ından gelir.** Sayfalı bir uç kurduğunda toplam
|
|
111
|
+
kayıt sayısı gövdede değil header'dadır (`microservice_execute` yanıtında `headers` altında,
|
|
112
|
+
küçük harfle `total-count`). Tabloya bağlarken sayfa göstergesinin okuduğu sözleşme budur;
|
|
113
|
+
ayrıntı `platform_guide('page')` sayfalama bölümü. Alternatif olarak sorgunun kendisi
|
|
114
|
+
`COUNT(*) OVER()` ile toplamı satıra koyabilir — o zaman header'a hiç bağımlı kalmazsın.
|
|
115
|
+
|
|
59
116
|
5. **Doğrulama akışı:** `microservice_preview` (kaydetmeden) → kaydet → `microservice_execute`
|
|
60
117
|
(gerçek runtime yolu) → sonuç beklenmedikse **aynı parametrelerle `microservice_debug`** —
|
|
61
118
|
adım adım executionItems ağacı döner.
|
|
62
119
|
|
|
120
|
+
## Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a bağlanır (MS-018, block)
|
|
121
|
+
|
|
122
|
+
`returnDtoId` · `parameterDtoId` · `returnBehavior` · `parameterBehavior` **microservice
|
|
123
|
+
komutunda yoktur** (`Create/UpdateMicroserviceCommand`). Gövdeye yazarsan platform 200 döner ve
|
|
124
|
+
alanı sessizce düşürür: uç sözleşmesiz kalır, ne hata ne uyarı gelir (2026-09-09 koşusu `S-2`).
|
|
125
|
+
Gateway bu gövdeyi artık MS-018 ile **platforma hiç göndermeden** reddeder.
|
|
126
|
+
|
|
127
|
+
Yanıt şeklini kaynak belirler:
|
|
128
|
+
|
|
129
|
+
| Kaynak | Yanıt şeklini ne belirler | Açık DTO sözleşmesi |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `table` | `sourceParameters.selectFields` (+ join alanları) | yok |
|
|
132
|
+
| `query` | sorgunun `returnScheme`'i — kayıt anında SQL'den üretilir | yok |
|
|
133
|
+
| `view` | view'ın indekslediği alanlar + birincil anahtar (`selectFields` ile genişletilir) | yok |
|
|
134
|
+
| `workflow` | akışın `returnDtoId` + `returnBehavior` sözleşmesi | **var** |
|
|
135
|
+
|
|
136
|
+
Yani "yanıtın alanları şu DTO'dan olsun" diyeceksen uç **workflow kaynaklı** kurulur:
|
|
137
|
+
|
|
138
|
+
1. `dto_list` ile mevcut DTO'yu bul (platform entity başına DTO üretir) ya da `dto_create` → `dtoId`
|
|
139
|
+
2. `workflow_create` → `returnDtoId: dtoId` + `returnBehavior: 'object' | 'array' | 'primitive'`.
|
|
140
|
+
WF-002: `type:'workflow'` akışta `returnDtoId` **zorunludur** (yoksa akış `unready` kalır);
|
|
141
|
+
`returnBehavior`'ı boş bırakırsan gateway `object` ile doldurup `gatewayNormalized` der.
|
|
142
|
+
3. `microservice_create` → `source: 'workflow'` + `workflowId` — DTO alanı **uca yazılmaz**.
|
|
143
|
+
4. Aynı uç `middlewareIds` ile kimlik kapsamasını da taşır (§"Kimlik kapsaması × uç kaynağı").
|
|
144
|
+
|
|
145
|
+
Kayıtlı bir akışa sözleşmeyi sonradan bağlamak tek çağrıdır:
|
|
146
|
+
`workflow_patch(id: <workflowId>, ops: [{ op: 'add', path: '/returnDtoId', value: <dtoId> }, { op: 'add', path: '/returnBehavior', value: 'object' }])`.
|
|
147
|
+
|
|
148
|
+
**Sözleşmenin bağlandığını uçtan oku:** workflow kaynaklı uçta `microservice_get` yanıtındaki
|
|
149
|
+
`returnScheme` **DTO'nun alanlarını** taşır; table/query/view kaynaklı uçta şema kaynaktan türer.
|
|
150
|
+
Bağlanmamış sözleşmenin belirtisi budur — uç 200 döner ama `returnScheme` DTO'yu değil kaynağı
|
|
151
|
+
yansıtır (2026-09-09'da ölçüldü: `returnDtoId` yazılan table kaynaklı uçta şema `['id','name']`
|
|
152
|
+
kaldı, DTO'nun beş alanı gelmedi).
|
|
153
|
+
|
|
154
|
+
Sözleşme yalnız **2xx** yanıtlarda uygulanır: 401/422 döndüren bir `response` adımının gövdesi
|
|
155
|
+
DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözleşme **süzer, uydurmaz**:
|
|
156
|
+
`bodyExpression`'ın ürettiği DTO dışı alan yanıttan düşer, DTO'da olup üretilmeyen alan gelmez
|
|
157
|
+
(ölçüm: `gizliNot` alanı yanıtta yok, `slot_minutes` de yok).
|
|
158
|
+
|
|
63
159
|
## Asgari doğru table-source microservice (platformda doğrulanmış kurallar)
|
|
64
160
|
|
|
65
161
|
- `operation` enum'u **tam olarak** şudur ([MicroserviceTableOperation](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Studio/Microservices/MicroserviceTableOperation.cs)):
|
|
@@ -69,12 +165,26 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
|
|
|
69
165
|
yapmasını iki ayrı uç + akışta condition dallanmasıyla kurmayın: tek `operation: 'upsert'`
|
|
70
166
|
ucu yeterlidir. Aynı işlem workflow **table step**'inde de desteklenir
|
|
71
167
|
([TableStep](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Flow/Workflows/Steps/TableStep.cs)).
|
|
168
|
+
|
|
169
|
+
- **Upsert eşleşmesi `filters` ile kurulur, PK `fields`'a KONMAZ** (ölçüm: İK koşusu M-4,
|
|
170
|
+
2026-09-01). PK'yı `fields`'a koyarsan `AlwaysGeneratedFieldCantBeWritten` alırsın. Doğru şekil:
|
|
171
|
+
`filters`'a `source: "body"` + `isRequired: false` bir id filtresi; gövdede id **varsa** eşleşen
|
|
172
|
+
satır güncellenir, **yoksa** yeni satır açılır. `isRequired: true` yaparsan "yeni kayıt" yolu
|
|
173
|
+
kapanır (id gelmeyince uç reddeder).
|
|
174
|
+
|
|
175
|
+
> **Upsert'te üretilen alan tuzağı.** `operation: 'upsert'` UPDATE yolunda da alan ifadelerini
|
|
176
|
+
> yeniden değerlendirir; sicil/kod gibi üretilen bir değeri `source: 'expression'` ile yazarsan
|
|
177
|
+
> güncellemede **sessizce değişir** (İK koşusu M-3: EMP-0005 → EMP-0006). Alan bazlı
|
|
178
|
+
> `writeOn: create | update | always` platformda **yok**; o gelene kadar oluşturma ve güncelleme
|
|
179
|
+
> için iki ayrı uç kurun. Gateway **MS-016** ile uyarır.
|
|
72
180
|
- **`count` vardır** — kayıt sayısı için tüm satırları çekip uzunluğuna bakmayın; panel/KPI
|
|
73
181
|
sayaçlarının doğru şekli budur.
|
|
74
182
|
- Gövde kökünde **`entityId` zorunlu**. (Eski "kökte datasourceId zorunlu" bilgisi yanlıştı —
|
|
75
183
|
komutta öyle bir alan yok, gönderilirse yok sayılır; asıl zorunluluk selectFields'taki fieldId.)
|
|
76
|
-
- `selectFields[]`
|
|
77
|
-
|
|
184
|
+
- `selectFields[]` / `filters[]` öğelerinde platform **`fieldId`** ister; gateway'e **`fieldSlug`**
|
|
185
|
+
vermen yeter — kök entity'nin alanları için id'yi gateway çözer ve `gatewayResolved` ile bildirir
|
|
186
|
+
(join'lenen entity'lerin alanlarında hâlâ `fieldId` gerekir). Kök `entityId` de slug olabilir.
|
|
187
|
+
Kök tablo alanlarında alias = **entity'nin slug'ı** ve `path` **yazılmaz**. Alias verilmezse
|
|
78
188
|
`ArgumentNullException(key)`, farklı alias verilirse "missing FROM-clause entry" gelir.
|
|
79
189
|
- `sort` ve `limit` **her zaman** verilir (MS-008, block) — aşağıya bak.
|
|
80
190
|
- Filtrelerde `value` bir **JavaScript ifadesidir** — sabit metin tırnak içinde
|
|
@@ -90,6 +200,11 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
|
|
|
90
200
|
`{ alias, fieldId, fieldSlug }`). Aynı kural workflow table create/update step'lerinde de
|
|
91
201
|
geçerlidir. Boş `returnFields` ile write operasyonu yazarsan MS-009 uyarır; buna karşılık
|
|
92
202
|
write operasyonunda boş `selectFields` **uyarı üretmez** (platform onu zaten yok sayar).
|
|
203
|
+
- **Platformun ürettiği alanları `fields` içine koymayın.** Primary key/identity gibi
|
|
204
|
+
`alwaysGenerated` alanlar yazılırsa istek 400 döner ve hatalı alan slug'larını sayar; bu alanlar
|
|
205
|
+
create'te platformca üretilir, upsert eşleşmesi gerekiyorsa değer `fields` yerine `filters`ta
|
|
206
|
+
taşınır. Incremental alanın create/update zamanlaması ayrı tasarım konusudur; mevcut sınır için
|
|
207
|
+
[`writeOn` tasarımına](../../../../zeyrek-backend/docs/alan-yazma-zamani/01-tasarim.md) bakın.
|
|
93
208
|
|
|
94
209
|
```jsonc
|
|
95
210
|
{
|
|
@@ -208,6 +323,19 @@ karşılamayı imkânsız kılar. Karar kuralı:
|
|
|
208
323
|
"isRequired": false, "children": [], "order": 0 }
|
|
209
324
|
```
|
|
210
325
|
|
|
326
|
+
> **`source: "query-string"` / `"body"` / `"header"` filtresinde `value` VERİLMEZ** (ölçüm: İK
|
|
327
|
+
> koşusu M-2, 2026-09-01). Değer çağrıdan gelir, parametre adı alan slug'ından türer. `value`
|
|
328
|
+
> yazarsan platform onu JS ifadesi sanıp eval eder → `JavaScriptException: <ad> is not defined`
|
|
329
|
+
> ile 500. `value` yalnız `source: "expression"` ve `"static"` kaynaklarında anlamlıdır. Sabit
|
|
330
|
+
> ön-süzme (tenant, aktiflik) `expression` ile yapılır. Gateway **MS-015** lint'i bu
|
|
331
|
+
> kombinasyonu istek platforma gitmeden yakalar.
|
|
332
|
+
|
|
333
|
+
> **Dış kaynaklı filtreye (`query-string` / `body` / `header`) `value` yazılmaz.** Değer çağrıdan
|
|
334
|
+
> gelir; tanımdaki `value` çalışma zamanında yok sayılır — parametre gelmezse filtre atlanır ve uç
|
|
335
|
+
> **süzülmemiş** tam liste döner (2026-09-03 öncesinde aynı alan JS olarak değerlendirilip 500
|
|
336
|
+
> veriyordu; İK koşusu M-2). Tanımdan gelen sabit değer istiyorsan `source: 'static'` ya da
|
|
337
|
+
> `'expression'` kullan.
|
|
338
|
+
|
|
211
339
|
### Dinamik filtre (`enableDynamicFilters`) — süzülebilir tablonun uç şekli
|
|
212
340
|
|
|
213
341
|
`enableDynamicFilters: true` verilen uç, süzme koşullarını **tanımdan değil çağrıdan** alır.
|
|
@@ -267,12 +395,69 @@ hem `joins` ile getirin hem `selectFields`'a koyun; ondan sonra `filters[0][fiel
|
|
|
267
395
|
> (qm kanıtı: `sy-users-get-my-profile`). Ayrıntı ve tarifler: `platform_guide('middleware')`.
|
|
268
396
|
> Middleware bağlamadan `user.` okuyan expression yazarsan MS-011 uyarır — runtime'da
|
|
269
397
|
> `user is not defined` 500'ü yersin.
|
|
398
|
+
>
|
|
399
|
+
> **`middlewareIds`'i `microservice_create` GÖVDESİNDE ver, sonradan patch'leme.** Ucu kurup
|
|
400
|
+
> ardından `microservice_patch` ile `/middlewareIds` yazmak her uç için fazladan bir tur demektir:
|
|
401
|
+
> 2026-09-06 koşusunda dokuz uç kuruldu ve dokuz patch çağrısının dokuzu da yalnız bu yolu yazdı
|
|
402
|
+
> (~3 dk). Alan create gövdesinde zaten kabul ediliyor.
|
|
270
403
|
|
|
271
404
|
> Sayfa tarafındaki karşılığı: kullanıcı ekrandan süzüyorsa (`table` + `filterMode` açık, ya da
|
|
272
405
|
> `datatable` + `isFilterable` kolonlar) uç `enableDynamicFilters: true` olmalı; komponent seçilen
|
|
273
406
|
> koşulları kendi `filters` prop'una yazar ve akış onu uca `filters` parametresi olarak geçirir.
|
|
274
407
|
> Süzme yoksa statik query-string filtreleri yeterlidir — bkz. `platform_guide('page')` Kural 1e.
|
|
275
408
|
|
|
409
|
+
### Kimlik kapsaması × uç kaynağı — query kaynaklı uç BAĞLAM GÖRMEZ
|
|
410
|
+
|
|
411
|
+
Yukarıdaki `source: 'expression'` + `value: 'user.id'` kalıbı **yalnız table kaynaklı** uçlarda
|
|
412
|
+
vardır. Agregasyon/rapor uçları query kaynaklı olduğu için aynı kalıp orada yoktur ve
|
|
413
|
+
**parametrelerinin tamamı çağrıdan gelir** — kapsamı gövdeye yazan bir uç sahtelenebilir.
|
|
414
|
+
|
|
415
|
+
| Uç kaynağı | Parametre/filtre nereden gelir | Kapsam sunucuda zorlanır mı |
|
|
416
|
+
|---|---|---|
|
|
417
|
+
| `table` | `filters[]` · `source:'expression'`, `value:'user.id'` | **Evet** (MS-011 middleware bağını denetler) |
|
|
418
|
+
| `query` | Yalnız çağrının kendisi: **body → query-string → header** (`Query.PrepareQueryParameters` → `FindValue`) | **Hayır** — ifade değerlendirmesi yoktur |
|
|
419
|
+
| `workflow` | Adım ifadeleri; `user.*` bağlı middleware'den gelir | **Evet** (ölçüldü 2026-09-07) |
|
|
420
|
+
| `view` | Arama sözleşmesi (`searchText`) + view'ın indekslediği alanlar | **ölçülmedi** — kapsam şartsa workflow yolunu kullan |
|
|
421
|
+
|
|
422
|
+
Query parametresine ifade kaynağı **eklenemez**: `parameters[].source` komut şemasında yoktur,
|
|
423
|
+
platform anahtarı sessizce atar (gateway `unmappedMembers` ile söyler) ve kayıtlı şema
|
|
424
|
+
`source: 'body'` kalır. Bu bir eksik değil, modülün tanımı: query modülü SQL seviyesinde çalışan
|
|
425
|
+
ham SQL yüzeyidir, istek bağlamını bilmez.
|
|
426
|
+
|
|
427
|
+
**Ölçüm (2026-09-07, aynı sorgu, aynı middleware — middleware `user.owner_key = 'OWNER-A'` kurdu):**
|
|
428
|
+
|
|
429
|
+
| Uç | Gövde | Yanıt |
|
|
430
|
+
|---|---|---|
|
|
431
|
+
| `source:'query'` | `{ owner_key:'OWNER-A' }` | `{ owner_key:'OWNER-A', row_count:3, total:60 }` |
|
|
432
|
+
| `source:'query'` | `{ owner_key:'OWNER-B' }` | `{ owner_key:'OWNER-B', row_count:2, total:300 }` ← **başkasının verisi** |
|
|
433
|
+
| `source:'workflow'` | `{ owner_key:'OWNER-B' }` | `{ owner_key:'OWNER-A', row_count:3, total:60 }` ← gövde **yok sayıldı** |
|
|
434
|
+
| `source:'workflow'` | `{ owner_key:'OWNER-B', min_amount:20 }` | `{ owner_key:'OWNER-A', row_count:2, total:50 }` ← kapsam dışı filtre gövdeden okundu |
|
|
435
|
+
|
|
436
|
+
#### Reçete: kapsanmış agregasyon ucu
|
|
437
|
+
|
|
438
|
+
Sorguyu değiştirmeye gerek yok; onu **bir katman yukarıdan** çağırırsın.
|
|
439
|
+
|
|
440
|
+
1. `query_create` — agregasyonu kapsam parametresiyle yaz:
|
|
441
|
+
`… WHERE company_id = @company_id AND created_at >= @from`, `parameters` ile her belirteci bildir.
|
|
442
|
+
2. `workflow_create` (`type: 'workflow'`) — start → **query adımı** → response. Adımın bağlaması
|
|
443
|
+
kapsamı middleware'den, kalan filtreleri gövdeden alır:
|
|
444
|
+
`rule.expression = "{ company_id: user.company_id, from: input.from }"`
|
|
445
|
+
(bağlamanın iki yazımı ve canlı motorun tuzağı: `platform_guide('workflow')` §"Query adımı").
|
|
446
|
+
3. `microservice_create` — `source: 'workflow'`, `workflowId`, **`middlewareIds` dolu**.
|
|
447
|
+
4. Doğrula: aynı ucu gövdeye **başka birinin** kapsam değerini yazarak çağır; yanıt değişmemeli.
|
|
448
|
+
|
|
449
|
+
`middlewareIds` boşken bu akış `500 JavaScriptException: user is not defined` verir (ölçüldü) —
|
|
450
|
+
MS-011'in workflow tarafındaki karşılığıdır ve gateway o dizinin boş kalmasını göremez, çünkü
|
|
451
|
+
uçtan görünen tek şey `workflowId`'dir.
|
|
452
|
+
|
|
453
|
+
Middleware tarafındaki karşılığı ve `user` değişkenini kuran akış:
|
|
454
|
+
`platform_guide('middleware')` Tarif 3c.
|
|
455
|
+
|
|
456
|
+
**Her agregasyonu sarmalama.** Kapsam gerekmeyen bir özet (herkese açık istatistik, zaten
|
|
457
|
+
middleware'in reddettiği bir panelde tüm şirketleri gösteren yönetici raporu) düz `source:'query'`
|
|
458
|
+
kalır: fazladan workflow, DTO ve response adımı bedavaya gelmez. Ölçü basit — **bu parametreyi
|
|
459
|
+
çağıran seçebilmeli mi?** Hayırsa sarmala.
|
|
460
|
+
|
|
276
461
|
### `sort` ve `limit` her zaman verilir
|
|
277
462
|
|
|
278
463
|
**MS-008 (block).** Platform bunları null bırakır; Studio formu ise `data.sort.name` /
|
|
@@ -285,6 +470,24 @@ Gateway bu kuralları MS-005/MS-006 olarak istek gitmeden denetler; platform val
|
|
|
285
470
|
hatası alırsan `microservice_debug` çıktısıyla raporla — o zaman gerçek bir bug'dır.
|
|
286
471
|
Query source'a kaçış yalnız ham SQL şartsa; query yazarken parser `SELECT` sonrası satır sonu
|
|
287
472
|
kabul etmez — ilk kolon `SELECT` ile aynı satırda olmalı ("Please use a supported query type").
|
|
473
|
+
Kayıtlı query'nin `@ad` / `{{ad}}` belirteçleri uçta **body → query-string → header** sırasıyla
|
|
474
|
+
bağlanır; bunun için parametreler query kaydedilirken `parameters` ile bildirilmiş olmalıdır
|
|
475
|
+
(`platform_guide('workflow')` §"Query adımı"). Davranış `query_preview` ile aynıdır.
|
|
476
|
+
|
|
477
|
+
**`language` AYRILMIŞ bir parametredir.** Ham sorguda `{{language}}` (ya da `@language`)
|
|
478
|
+
kullanırsan platform onu isteğin diliyle kendisi doldurur — `parameters` listesine yazmana gerek
|
|
479
|
+
YOKTUR ve yazarsan doğrulama onu zorunlu saymaz. Çok dilli companion tablosunu elle join eden bir
|
|
480
|
+
raporun dili böyle alınır:
|
|
481
|
+
|
|
482
|
+
```sql
|
|
483
|
+
SELECT p.id, COALESCE(l.title, d.title) AS title
|
|
484
|
+
FROM products p
|
|
485
|
+
LEFT JOIN products_localization l ON l.product_id = p.id AND l.language = {{language}}
|
|
486
|
+
LEFT JOIN products_localization d ON d.product_id = p.id AND d.language = 'en'
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
`query_preview` da aynı parametreyi ekleyerek koşar, yani şema çıkarımı `{{language}}` yüzünden
|
|
490
|
+
"eksik parametre" diye patlamaz.
|
|
288
491
|
|
|
289
492
|
## Middleware'siz uçta çok adımlı yazım ATOMİK DEĞİL (ölçüm 2026-08-19)
|
|
290
493
|
|
|
@@ -336,13 +539,23 @@ kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware
|
|
|
336
539
|
- `operation: 'list'` → geçersiz; `get` + `isSingle: false` kullanın (PLT-006)
|
|
337
540
|
- Üst seviye `entityId` eksik (MS-004, warn)
|
|
338
541
|
- Gerekçesiz `source: 'query'` (MS-002, warn)
|
|
542
|
+
- Backend zorunlulukları (MS-017, block — istek gitmeden): `name` ve `url` boş olamaz (update'te
|
|
543
|
+
`slug` da); `queryId`/`entityId`/`workflowId` verilirse boş GUID olamaz; `source: 'expression'`
|
|
544
|
+
alan ve filtrede `value` dolu; tekil filtrede `fieldId`; `writeOn` yalnız `operation: 'upsert'`
|
|
545
|
+
ucunda; join'de `entityId` + `fieldId`. Tam katalog: `ngapps://rules/backend-constraints`.
|
|
546
|
+
- Query kaydı (QRY-001, block): `rawQuery`'deki her `@ad` / `{{ad}}` `parameters[]`'ta
|
|
547
|
+
`{ name, type }` ile bildirilir — yalnız create denetler, update sessiz geçer ve sorgu çalışma
|
|
548
|
+
anında düşer; `slug`/`rawQuery`/`datasourceId` zorunlu; `outputType` `array` | `object`.
|
|
339
549
|
- Execute'ta dönen `httpStatus` microservice'in kendi sonucudur — 4xx/5xx görünce önce
|
|
340
550
|
`microservice_debug` ile hangi adımın ürettiğine bak, tanımı ona göre düzelt.
|
|
341
|
-
- **`microservice_update` / `microservice_patch
|
|
342
|
-
HER ZAMAN 500
|
|
343
|
-
|
|
344
|
-
`
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
`
|
|
348
|
-
|
|
551
|
+
- **`microservice_update` / `microservice_patch` tüm operasyon tiplerinde çalışır.** Eski
|
|
552
|
+
"`operation: 'get'` tipli kayıtta HER ZAMAN 500 (`Application cannot be tracked`)" kuralı P-02
|
|
553
|
+
regresyonuydu; 2026-08-16'da düzeltildi, 2026-09-01 İK koşusunda get ucunda
|
|
554
|
+
`microservice_patch` ile canlı doğrulandı (M-9). Get-tipi bir microservice'i değiştirmek için
|
|
555
|
+
artık delete+create gerekmez. `entity_patch`, `workflow_patch`, `row_update` sağlamdır.
|
|
556
|
+
- **`microservice_patch` tam gövde geri yazar: okunan kayıtta olmayan alan SİLİNİR.** 09-04
|
|
557
|
+
koşusunda gidiş-dönüşte `description` düştü ve MS-014 patch'i bloke etti (Y-16). Gateway artık
|
|
558
|
+
açıklamasız bir patch gövdesini hiç göndermez; hata mesajı düzeltmeyi verir
|
|
559
|
+
(`{ "op": "add", "path": "/description", "value": "…" }`). Patch yanıtındaki `silentlyDropped`
|
|
560
|
+
alanı, gönderilen ama kaydedilmeyen alanları listeler — boş değilse platform bir alanı yutmuş
|
|
561
|
+
demektir.
|