@appaflytech/zeyrek-ai-mcp 0.0.1
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 +46 -0
- package/README.md +260 -0
- package/dist/config.js +128 -0
- package/dist/index.js +88 -0
- package/dist/knowledge/componentCatalog.js +438 -0
- package/dist/knowledge/componentEvents.js +164 -0
- package/dist/knowledge/coverage.js +60 -0
- package/dist/knowledge/normalize.js +1309 -0
- package/dist/knowledge/outlines.js +167 -0
- package/dist/knowledge/policy.js +44 -0
- package/dist/knowledge/reservedTables.js +75 -0
- package/dist/knowledge/rules.js +1725 -0
- package/dist/observability/trace.js +337 -0
- package/dist/platform/errors.js +132 -0
- package/dist/platform/http.js +169 -0
- package/dist/platform/responses.js +94 -0
- package/dist/platform/token.js +79 -0
- package/dist/resources/examples/entity-pair-m2m.json +105 -0
- package/dist/resources/examples/flow-list-to-form.json +65 -0
- package/dist/resources/examples/flow-load-table.json +178 -0
- package/dist/resources/examples/microservice-table-list.json +49 -0
- package/dist/resources/examples/middleware-authenticate.json +194 -0
- package/dist/resources/examples/page-detail-profile.json +95 -0
- package/dist/resources/examples/page-dynamic-form.json +221 -0
- package/dist/resources/examples/page-form-upsert.json +279 -0
- package/dist/resources/examples/page-html-embed.json +106 -0
- package/dist/resources/examples/page-sign-in.json +194 -0
- package/dist/resources/examples/workflow-basic.json +94 -0
- package/dist/resources/guides/component.md +108 -0
- package/dist/resources/guides/entity.md +348 -0
- package/dist/resources/guides/expressions.md +139 -0
- package/dist/resources/guides/getting-started.md +109 -0
- package/dist/resources/guides/html.md +106 -0
- package/dist/resources/guides/microservice.md +313 -0
- package/dist/resources/guides/middleware.md +271 -0
- package/dist/resources/guides/modify-existing.md +35 -0
- package/dist/resources/guides/page.md +1574 -0
- package/dist/resources/guides/view.md +89 -0
- package/dist/resources/guides/workflow.md +359 -0
- package/dist/resources/knowledge.js +44 -0
- package/dist/resources/schemas/CreateDatasourceCommand.json +54 -0
- package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +133 -0
- package/dist/resources/schemas/CreateEntityCommand.json +387 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +528 -0
- package/dist/resources/schemas/CreateQueryCommand.json +43 -0
- package/dist/resources/schemas/CreateViewCommand.json +158 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +1719 -0
- package/dist/resources/schemas/Filter.json +124 -0
- package/dist/resources/schemas/GetRowsQuery.json +122 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +565 -0
- package/dist/resources/schemas/PreviewQueryCommand.json +74 -0
- package/dist/resources/schemas/PreviewWorkflowCommand.json +981 -0
- package/dist/resources/schemas/StepDto.json +900 -0
- package/dist/resources/schemas/UpdateDatasourceCommand.json +58 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +679 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +512 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +86 -0
- package/dist/resources/schemas/UpdateViewCommand.json +153 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1719 -0
- package/dist/resources/schemas.js +40 -0
- package/dist/server.js +63 -0
- package/dist/tools/access.js +27 -0
- package/dist/tools/applications.js +59 -0
- package/dist/tools/automations.js +103 -0
- package/dist/tools/common.js +18 -0
- package/dist/tools/components.js +280 -0
- package/dist/tools/datasources.js +82 -0
- package/dist/tools/dependencies.js +17 -0
- package/dist/tools/deployments.js +17 -0
- package/dist/tools/dtos.js +54 -0
- package/dist/tools/entities.js +166 -0
- package/dist/tools/files.js +138 -0
- package/dist/tools/flows.js +226 -0
- package/dist/tools/folders.js +51 -0
- package/dist/tools/layouts.js +105 -0
- package/dist/tools/microservices.js +198 -0
- package/dist/tools/organizations.js +65 -0
- package/dist/tools/pages.js +597 -0
- package/dist/tools/platformInfo.js +44 -0
- package/dist/tools/projects.js +152 -0
- package/dist/tools/queries.js +48 -0
- package/dist/tools/rows.js +115 -0
- package/dist/tools/screens.js +97 -0
- package/dist/tools/templates.js +65 -0
- package/dist/tools/variables.js +94 -0
- package/dist/tools/views.js +58 -0
- package/dist/tools/workflows.js +196 -0
- package/package.json +49 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# View Rehberi (tam metin arama)
|
|
2
|
+
|
|
3
|
+
View, **serbest metin araması** içindir: seçtiğin alanlar bir analizörden geçirilip `slug` adlı
|
|
4
|
+
projeksiyon tablosuna indekslenir. **Raporlama/agregasyon aracı değildir** — özet/sayım için
|
|
5
|
+
`query_create` (raw SQL) + query kaynaklı microservice kullan.
|
|
6
|
+
|
|
7
|
+
Gövde şemaları: `ngapps://schemas/CreateViewCommand` · `ngapps://schemas/UpdateViewCommand`.
|
|
8
|
+
|
|
9
|
+
## Kuruluş sırası
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
1. datasource_list (datasourceId — create'te zorunlu, update'te alan YOK)
|
|
13
|
+
2. entity_get (indekslenecek alanların fieldId'leri)
|
|
14
|
+
3. view_create
|
|
15
|
+
4. microservice_create (source: 'view') ← tüketim yolu
|
|
16
|
+
5. view_rebuild (toplu veri değişikliğinden sonra)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Çalışan gövde
|
|
20
|
+
|
|
21
|
+
```jsonc
|
|
22
|
+
{
|
|
23
|
+
"name": "Talep Arama", "slug": "ticket_search",
|
|
24
|
+
"datasourceId": "<datasource id>", "providerType": "postgres",
|
|
25
|
+
"analyzer": { "language": "turkish", "tokenizer": "standard",
|
|
26
|
+
"charFilters": [], "tokenFilters": ["lowercase", "asciifolding"] },
|
|
27
|
+
"entities": [{ "entityId": "<tickets entity id>", "alias": "tickets" }],
|
|
28
|
+
"fields": [
|
|
29
|
+
{ "viewEntityAlias": "tickets", "fieldId": "<title field id>", "alias": "title", "weight": 2 },
|
|
30
|
+
{ "viewEntityAlias": "tickets", "fieldId": "<description field id>", "alias": "body", "weight": 1 }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `analyzer` verilmezse platform `ViewAnalyzer.Default()` kullanır — Türkçe içerikte
|
|
36
|
+
`language: 'turkish'` + `tokenFilters: ['lowercase','asciifolding']` açıkça verilmelidir, yoksa
|
|
37
|
+
"Talep" ile "talep" ya da "yazilim" ile "yazılım" eşleşmez.
|
|
38
|
+
- `fields[].weight` sıralama ağırlığıdır (`double`): başlık 2, gövde 1 gibi.
|
|
39
|
+
- `fields[].viewEntityAlias`, `entities[].alias` ile birebir eşleşmeli. Eşleşmezse alan **sessizce
|
|
40
|
+
ilk tabloya** bağlanır (`ViewSourceMapper.Map` → `tablesByAlias.GetValueOrDefault(...) ?? tables.FirstOrDefault()`).
|
|
41
|
+
- `view_get` alanları `viewEntityId` ile döndürür, yazma tarafı `viewEntityAlias` ister; gateway
|
|
42
|
+
çeviriyi yapar, çeviremezse isteği reddeder.
|
|
43
|
+
|
|
44
|
+
## Enum sözlüğü (hepsi küçük harf)
|
|
45
|
+
|
|
46
|
+
| Alan | Değerler |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `providerType` | `postgres` |
|
|
49
|
+
| `analyzer.language` | `simple` · `turkish` · `english` |
|
|
50
|
+
| `analyzer.tokenizer` | `standard` · `whitespace` · `edge-ngram` |
|
|
51
|
+
| `analyzer.charFilters[]` | `html-strip` |
|
|
52
|
+
| `analyzer.tokenFilters[]` | `lowercase` · `stopwords` · `stemmer` · `asciifolding` · `synonym` |
|
|
53
|
+
|
|
54
|
+
`edge-ngram` "yazarken arama" (typeahead) içindir ve indeksi büyütür; tam kelime araması için
|
|
55
|
+
`standard` yeterlidir.
|
|
56
|
+
|
|
57
|
+
## Çoklu entity JOIN DEĞİLDİR
|
|
58
|
+
|
|
59
|
+
`entities` birden çok tablo alır, ama aralarında **ilişki tanımı yoktur** — `ViewSourceMapper.Map`
|
|
60
|
+
yalnız tabloları ve alanları eşler. Her tablo ayrı ayrı indekslenir; eşleşme hangi tablodan
|
|
61
|
+
geldiyse dönen kayıt **o tablonun satırıdır**.
|
|
62
|
+
|
|
63
|
+
Yani *"talebi yorum içeriğinden bul"* bu yolla kurulamaz: yorumdan eşleşme gelirse elinde yorum
|
|
64
|
+
satırı olur, talep değil.
|
|
65
|
+
|
|
66
|
+
**Doğru desen — denormalize arama alanı:**
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
1. tickets'a bir arama alanı ekle (ör. search_blob, TEXT)
|
|
70
|
+
2. ticket_comments üzerinde create/update automation'ı: yorum metnini ilgili talebin
|
|
71
|
+
search_blob'una ekle (row.ticket_id ile talebi bul, mevcut blob + yeni yorum)
|
|
72
|
+
3. view tek entity (tickets) üzerinde title + description + search_blob alanlarını indeksler
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Bu kurulumda yalnız yorumda geçen bir kelime **talebi** döndürür. Automation tarifi:
|
|
76
|
+
`platform_guide('workflow')` §"Automation gerçekleri".
|
|
77
|
+
|
|
78
|
+
## Tüketim: `source: 'view'` microservice'i
|
|
79
|
+
|
|
80
|
+
View'i okumanın tek yolu view kaynaklı bir microservice'tir ve o uç **zorunlu `searchText`**
|
|
81
|
+
parametresi ister (+ `limit`/`offset`). Ayrıntı: `platform_guide('microservice')`.
|
|
82
|
+
|
|
83
|
+
## Bakım
|
|
84
|
+
|
|
85
|
+
- `view_rebuild` projeksiyonu sıfırdan kurar; toplu satır yüklemesinden ya da alan listesi
|
|
86
|
+
değişikliğinden sonra çağır.
|
|
87
|
+
- View'in `slug`'ı projeksiyon tablosunun/indekslerinin adıdır — slug değiştirmek onları yeniden
|
|
88
|
+
adlandırır; elle oluşturulmuş bir tabloyla çakışmasın diye entity slug'larıyla aynı isim uzayında
|
|
89
|
+
düşün (`platform_guide('entity')` §"Slug seçimi").
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Workflow Rehberi
|
|
2
|
+
|
|
3
|
+
Buradaki kurallar gerçek projelerde (qm, hr) **deneyerek doğrulandı**.
|
|
4
|
+
|
|
5
|
+
## Karar kuralları
|
|
6
|
+
|
|
7
|
+
1. **DTO önce (yalnız workflow tipinde).** `type='workflow'` için girdi/çıktı sözleşmesini
|
|
8
|
+
DTO olarak kur: `parameterDtoId` + `parameterBehavior`, `returnDtoId` + `returnBehavior`
|
|
9
|
+
(DtoId verilen her yerde Behavior da zorunlu). Platform her entity için `isDefault: true`
|
|
10
|
+
DTO üretir — önce `dto_list` ile ara, çoğu zaman yenisini yazmak gerekmez.
|
|
11
|
+
2. **Veri adımı seçimi: önce table.** Query step yalnız ham SQL şartsa.
|
|
12
|
+
3. **Step/branch id'leri gerçek GUID** olmalı (`"s-start"` gibi id'ler `System.Guid` çevrim
|
|
13
|
+
hatası verir) ve **veritabanında satır oldukları için global benzersiz** — başka akıştaki
|
|
14
|
+
GUID'i tekrar kullanmak `RowAlreadyExists` üretir; her akışa taze GUID bloğu ver.
|
|
15
|
+
4. **Kaydetmeden önce `workflow_preview`.** Test girdisi **`previewPayload`** anahtarıyla
|
|
16
|
+
verilir (`parameters`/`input` çalışmaz, ifadeler `undefined` üretir); değişkenler
|
|
17
|
+
`previewVariables`.
|
|
18
|
+
- **`previewPayload` bir STRING olmalı ve tek kanonik biçimi `"({ … })"`'dir.** Alan
|
|
19
|
+
JavaScript **kaynak metni** bekliyor (`PreviewWorkflowCommand` gövdeyi `jsEngine.Eval` ile
|
|
20
|
+
çalıştırır) ve **ifadenin sonucu bir NESNE olmak zorundadır**:
|
|
21
|
+
|
|
22
|
+
```jsonc
|
|
23
|
+
"previewPayload": "({ email: 'a@b.c', password: '1234' })" // ✔ tek doğru biçim
|
|
24
|
+
"previewPayload": { "email": "a@b.c" } // ✘ 500: Unexpected end of input
|
|
25
|
+
"previewPayload": "JSON.stringify({ email: 'a@b.c' })" // ✘ SESSİZ: sonuç string, input {} kalır
|
|
26
|
+
"previewPayload": "{ email: 'a@b.c' }" // ✘ SESSİZ: JS bunu BLOK olarak ayrıştırır
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Nesne göndermenin hatası gürültülüdür (`Unexpected end of input (<anonymous>:1:8x)`, sabit
|
|
30
|
+
sütun numarası, step'lerden bağımsız — step ifadelerini suçlamayın). **Sessiz varyant daha
|
|
31
|
+
tehlikelidir:** ifade nesne üretmezse preview `input: {}` ile koşar ve suçu adıma atan bir
|
|
32
|
+
mesaj alırsın (`Please provide the 'members.email' parameter.`) — asıl sorun girdinin hiç
|
|
33
|
+
bağlanmamış olmasıdır. Yanıttaki `"input"` alanının dolu olduğunu her koşumda kontrol et.
|
|
34
|
+
Gateway üç hatalı biçimi de **WF-016** ile istek gitmeden reddeder.
|
|
35
|
+
Automation önizlemesinde aynı biçimle `row`/`previousRow`/`operation`, middleware'de
|
|
36
|
+
`cookie`/`header`/`queryString`/`body`/`triggerSource` anahtarları verilir.
|
|
37
|
+
- **Preview YAZMAZ — transaction içinde koşup rollback eder.** Adımlar gerçek id'ler döndürür
|
|
38
|
+
ama koşum bitince o satırlar tabloda **yoktur** (ölçüm 2026-08-17: `users` + `employees` +
|
|
39
|
+
`leave_balances` yazan bir akış preview'da uçtan uca geçti, sonra `row_list` üçünde de
|
|
40
|
+
bulamadı). İki sonucu var: (1) yazma adımlı akışları çekinmeden önizleyebilirsin;
|
|
41
|
+
(2) **döndürdüğü id'ler kalıcı değildir** — o id ile `row_update` denersen
|
|
42
|
+
`Sequence contains no elements` alırsın ve hata seni entity'yi suçlamaya iter.
|
|
43
|
+
Kalıcı satır istiyorsan akışı bağlı ucundan `microservice_execute` ile çağır.
|
|
44
|
+
|
|
45
|
+
## Step'ler nasıl bağlanır
|
|
46
|
+
|
|
47
|
+
Her step'in `branches[]` dizisi vardır; `nextStepId` sonraki step'in id'sini gösterir.
|
|
48
|
+
Bağlanmamış step çalışmaz. `start` tek branch taşır; normal step'ler `success` + `fail`;
|
|
49
|
+
`condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`,
|
|
50
|
+
`end`) **branch taşımaz**.
|
|
51
|
+
|
|
52
|
+
## Condition step kuralları
|
|
53
|
+
|
|
54
|
+
Koşullar `rule.items[]` dizisindedir ve **sırayla** değerlendirilir; ilk `true` olan dala
|
|
55
|
+
gidilir, hiçbiri tutmazsa `else` dalı yürür:
|
|
56
|
+
|
|
57
|
+
```jsonc
|
|
58
|
+
{ "id": "<GUID-C>", "name": "status_check", "type": "condition",
|
|
59
|
+
"rule": { "stepType": "condition", "queryStepSourceType": "library", "isExpression": false,
|
|
60
|
+
"items": [
|
|
61
|
+
{ "expression": "load_row.data.length === 0", "branchId": "<GUID-B1>" },
|
|
62
|
+
{ "expression": "load_row.data[0].status !== 'Onaylandı'", "branchId": "<GUID-B2>" }
|
|
63
|
+
] },
|
|
64
|
+
"branches": [
|
|
65
|
+
{ "id": "<GUID-B1>", "name": "<GUID-B1>", "type": "success", "nextStepId": "<404 step>" },
|
|
66
|
+
{ "id": "<GUID-B2>", "name": "<GUID-B2>", "type": "success", "nextStepId": "<422 step>" },
|
|
67
|
+
{ "id": "<GUID-B3>", "name": "else", "type": "success", "nextStepId": "<devam step>" }
|
|
68
|
+
] }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- Her `items[].branchId` bir branch'in **id'sidir**; runtime bu eşleşmeyle dallanır.
|
|
72
|
+
- **Koşul branch'inin `name`'i, kendi id'sinin GUID string'i OLMALIDIR** (`else` dalı sabit
|
|
73
|
+
`"else"` kalır). Studio kenarları `branch.name` ↔ `items[].branchId` eşleşmesiyle çizer;
|
|
74
|
+
`randevu_yok` gibi anlamlı bir ad verilirse akış **runtime'da kusursuz çalışır ama Studio'da
|
|
75
|
+
koşullar bağlanmamış görünür** — sessiz bozulma, execute testi yakalayamaz. Okunabilir
|
|
76
|
+
adlandırmayı step adında yap; koşulların anlamı ifadelerinden okunur.
|
|
77
|
+
**Gateway bunu otomatik düzeltir:** `items[].branchId`'nin gösterdiği branch'in adını id'siyle
|
|
78
|
+
eşitler ve `gatewayNormalized` ile bildirir; eşleşmeyen bir `branchId` kalırsa WF-014 isteği
|
|
79
|
+
durdurur (o durumda koşul hiçbir dala bağlı değildir).
|
|
80
|
+
- İfadeler önceki step çıktılarıyla çalışır (`<stepAdı>.data…`); ilişki alanı nesne dönebilir,
|
|
81
|
+
`x.doctor_id.id ? x.doctor_id.id : x.doctor_id` kalıbı iki biçimi de karşılar.
|
|
82
|
+
|
|
83
|
+
## Üç workflow tipi
|
|
84
|
+
|
|
85
|
+
| type | Ne zaman | Tetiklenme | Özel kurallar |
|
|
86
|
+
|---|---|---|---|
|
|
87
|
+
| `workflow` | REST ucu gibi çağrılabilir iş akışı | HTTP (`/{app}/{slug}`) | `returnDtoId` zorunlu (WF-002) |
|
|
88
|
+
| `automation` | Entity satır olayına tepki | `entityId` + `operation` + `isQueue` | returnDtoId YASAK; aşağıdaki "Automation gerçekleri" |
|
|
89
|
+
| `middleware` | Microservice/sayfa **çalışmadan önce** koşan guard/setter (auth, `user`/`settings` değişkenleri) | Bağlandığı uç çağrılınca (`middlewareIds`) | returnDtoId gerekmez; `set-user`/`variable`/`setcookie`/`redirect` yalnız bu tipte. **Ayrı rehberi var: `platform_guide('middleware')`** |
|
|
90
|
+
|
|
91
|
+
## Step tipi × workflow tipi izin matrisi
|
|
92
|
+
|
|
93
|
+
| Step | workflow | automation | middleware |
|
|
94
|
+
|------|:-:|:-:|:-:|
|
|
95
|
+
| start, condition, code, loop, table, query, http, workflow, email, file-upload | ✓ | ✓ | ✓ |
|
|
96
|
+
| variable (Set Variable), set-user, setcookie, redirect | – | – | ✓ |
|
|
97
|
+
| response | ✓ | – | ✓ |
|
|
98
|
+
| convert | ✓ | – | – |
|
|
99
|
+
| revert (Fail and Rollback) | – | ✓ | – |
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## Loop step — dizinin her öğesi için bir adım koştur
|
|
103
|
+
|
|
104
|
+
Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seçenekler tek çağrıda;
|
|
105
|
+
ölçüldü 2026-08-19, `anket-admin/anket-kaydet`).
|
|
106
|
+
|
|
107
|
+
```jsonc
|
|
108
|
+
{ "id": "<GUID>", "name": "loopQuestions", "type": "loop",
|
|
109
|
+
"rule": {
|
|
110
|
+
"stepType": "table", // ÇOCUK adımın tipi (loop değil)
|
|
111
|
+
"arrayExpression": "input.questions || []", // JS ifadesi → dizi
|
|
112
|
+
"stepRule": { /* çocuğun kendi rule'u — burada table create */ }
|
|
113
|
+
},
|
|
114
|
+
"branches": [{ "id": "<GUID>", "name": "success", "nextStepId": "<sonraki>" }] }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- **Çocuk kural `stepRule`'dur, `children` DEĞİL.** (Gateway **WF-018** ile yakalar: `stepRule` yoksa
|
|
118
|
+
ya da `arrayExpression` boşsa istek gitmez.) API DTO'sunda alan tekildir
|
|
119
|
+
(`StepRuleDto StepRule`); `children` gönderirsen `rule.Children` boş kalır ve loop
|
|
120
|
+
**`NullReferenceException`** ile patlar — hata mesajı `arrayExpression`'ı işaret ettiği için
|
|
121
|
+
seni ifadeyi suçlamaya iter, oysa sorun anahtar adıdır.
|
|
122
|
+
- **Geçerli öğe `loopItem`'dır.** Çocuğun `fields[].value` / `filters[].value` ifadelerinde
|
|
123
|
+
`loopItem.<alan>` yazılır; `input` ve önceki adımlar (`createSurvey.data.id`) da görünmeye
|
|
124
|
+
devam eder.
|
|
125
|
+
- **Çıktı `[{ item, result }]` dizisidir.** `result` çocuk adımın çıktısıdır; `table create`
|
|
126
|
+
için `{ id }`. Sonraki adımda `loopQuestions.data` ile okunur — **yazılan kayıtların id'sini
|
|
127
|
+
girdideki öğeyle eşleştirmenin yolu budur.**
|
|
128
|
+
- **İkinci seviye eşleme için `code` adımına gerek yok:** ikinci loop'un `arrayExpression`'ı
|
|
129
|
+
zaten JS'tir, eşlemeyi orada yap:
|
|
130
|
+
```js
|
|
131
|
+
(loopQuestions.data || []).reduce(function(acc, q){
|
|
132
|
+
var qid = q.result && q.result.id;
|
|
133
|
+
(input.options || []).filter(function(o){ return o.qkey === (q.item && q.item.key); })
|
|
134
|
+
.forEach(function(o){ acc.push({ label: o.label, question_id: qid }); });
|
|
135
|
+
return acc; }, [])
|
|
136
|
+
```
|
|
137
|
+
İstemci alt kayıtları **düz** gönderir ve satırları `key`/`qkey` ile eşler — iç içe dizi
|
|
138
|
+
göndermek gerekmez (iç içe loop denenmedi; `loopItem` iç loop'ta gölgeleneceği için önerilmez).
|
|
139
|
+
|
|
140
|
+
> **Table step'te `create`/`update` satır değerleri `fields[]`'tedir ve JS İFADESİDİR.**
|
|
141
|
+
> `{ "fieldId": "<alan id>", "source": "expression", "value": "input.title", "order": 0 }` —
|
|
142
|
+
> `PrepareParameters` her `fields[].value`'yu `EvaluateExpression` ile koşturur
|
|
143
|
+
> (`TableStep.cs:392`). `delete` işleminde `fields` boş kalır, iş `filters[]`'dedir.
|
|
144
|
+
|
|
145
|
+
> **GUID alanları HEX olmak zorunda.** Okunabilirlik için blok öneki kullanırken (`f1000001-…`)
|
|
146
|
+
> hex olmayan harf koyma: `g1000001-…` gönderdiğimde platform
|
|
147
|
+
> `The JSON value could not be converted to System.Guid` ile reddetti. Geçerli önek harfleri
|
|
148
|
+
> yalnız `a`–`f`'tir.
|
|
149
|
+
|
|
150
|
+
## Table step kuralları
|
|
151
|
+
|
|
152
|
+
- `rule` içinde `datasourceId` + `entityId` zorunlu; `selectFields`/`filters` öğelerinde
|
|
153
|
+
**`fieldId` zorunlu** (yalnız `fieldSlug` yetmez → `EvaluateWorkflowStepFailedException`).
|
|
154
|
+
- **Table step JOIN kurabilir** ve alias/path disiplini microservice'inkiyle **birebir aynıdır**
|
|
155
|
+
(Studio aynı form bileşenlerini kullanır): `joins[].path` = `<kökEntityId>.<ilişkiAlanı…>`,
|
|
156
|
+
join'li `selectFields[].path` = kök id olmadan aynı zincir, kök alanlarda `path` yok.
|
|
157
|
+
Join'in çıktısı sonraki adımlarda **iç içe** gelir: `step.data[0].course_id.code`.
|
|
158
|
+
- **`rule.sort` ve `rule.limit` her table step'te verilir** — yoksa Studio'da workflow sayfası
|
|
159
|
+
`Cannot read properties of null (reading 'sortValue')` ile hiç açılmaz (**WF-013, block**).
|
|
160
|
+
- Join path disiplini burada da **MS-007 (block)** ile aynıdır.
|
|
161
|
+
- **Join varsa `rule.alias` (KÖK tablo alias'ı) zorunludur** ve kök entity'nin slug'ı olmalıdır:
|
|
162
|
+
join'in ON koşulunu `"<kökAlias>.<ilişkiAlanı>"` kurar, boş alias SQL'e `.<alan>` yazdırır ve
|
|
163
|
+
Postgres **`42601: zero-length delimited identifier`** ile reddeder — adım hiç çalışmaz, hata
|
|
164
|
+
sebebi söylemez. Gateway alias'ı kök `selectFields`'tan türetebiliyorsa doldurur, türetemezse
|
|
165
|
+
**WF-019** block'uyla isteği hiç göndermez. (Join'siz adımda alias eksikliği sorun çıkarmaz;
|
|
166
|
+
bu yüzden "join'siz çalışıyordu" gözlemi yanıltıcıdır.)
|
|
167
|
+
- **İlişki alanı bu bağlamda DÜZ FK'dır.** `row_list` ilişki alanını her zaman
|
|
168
|
+
`{ id, <displayAlanı> }` nesnesi döndürür (her ilişki için join'i kendisi üretir); table adımı
|
|
169
|
+
yalnız **`joins` ile bildirdiğin** ilişkiyi nesneye çevirir. Bildirilmeyen alanda
|
|
170
|
+
`<alan>.<altAlan>` okuması `undefined` verir — hata değil, SESSİZ boş sonuç (rol dizisi
|
|
171
|
+
boşalırsa yetki kapısı yanlış karar verir; crm koşumu B-1). Gateway **WF-020** ile uyarır.
|
|
172
|
+
Dayanıklı yazım: `var v = x.role_id && x.role_id.id ? x.role_id.id : x.role_id;`
|
|
173
|
+
|
|
174
|
+
```jsonc
|
|
175
|
+
// Öğrencinin bir dersteki notlarını, ders bilgisiyle birlikte çeken table step
|
|
176
|
+
"rule": {
|
|
177
|
+
"stepType": "table", "queryStepSourceType": "library", "isExpression": false,
|
|
178
|
+
"datasourceId": "<datasource id>", "entityId": "<grades entity id>",
|
|
179
|
+
"operation": "get", "alias": "grades", "isSingle": false,
|
|
180
|
+
"selectFields": [
|
|
181
|
+
{ "alias": "grades", "fieldId": "<score id>", "fieldSlug": "score" },
|
|
182
|
+
{ "alias": "grades", "fieldId": "<weight id>", "fieldSlug": "weight" },
|
|
183
|
+
{ "alias": "courses", "fieldId": "<code id>", "fieldSlug": "code", "path": "<grades.course_id>" }
|
|
184
|
+
],
|
|
185
|
+
"joins": [
|
|
186
|
+
{ "alias": "courses", "entityId": "<courses id>", "fieldId": "<grades.course_id>",
|
|
187
|
+
"path": "<grades entity id>.<grades.course_id>", "joins": [] }
|
|
188
|
+
],
|
|
189
|
+
"filters": [
|
|
190
|
+
{ "alias": "grades", "fieldId": "<student_id>", "operator": "eq", "source": "expression",
|
|
191
|
+
"value": "input.student_id", "filterType": "single", "conditionType": "and",
|
|
192
|
+
"isRequired": true, "children": [], "order": 0 }
|
|
193
|
+
],
|
|
194
|
+
"fields": [], "returnFields": [],
|
|
195
|
+
"sort": { "name": "sortField", "type": "sortType", "source": "query-string" },
|
|
196
|
+
"limit": { "name": "limit", "offset": "offset", "source": "query-string" }
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
- **Filtre `value` alanı JavaScript ifadesidir** (eval edilir): sabit → `"'EMP004'"` (tırnak
|
|
200
|
+
içinde), dinamik → `"input.employee_no + '|' + input.year"`, ternary/`new Date()` çalışır.
|
|
201
|
+
Tırnaksız sabit `X is not defined` hatası verir. İfadelerde kullanılabilen platform
|
|
202
|
+
fonksiyonları (`system.getPageUrl`, `file.excel`, `UtcNow()`, `crypto.*`…) ve bağlam için
|
|
203
|
+
**`platform_guide('expressions')`** — uydurma fonksiyon adı runtime 500'üdür.
|
|
204
|
+
- Yazma step'lerinde (create/update): **yazılacak** alanlar `fields`, **okunacak** alanlar
|
|
205
|
+
`selectFields` — aynı alan ikisinde birden olamaz (`An item with the same key...`).
|
|
206
|
+
- **Yazma step'inin çıktısı yalnız primary key'dir** (`<step>.data.id`); sonraki step'lerin ya
|
|
207
|
+
da response'un başka alana ihtiyacı varsa bunları **`returnFields`** ile iste
|
|
208
|
+
(`{ alias, fieldId, fieldSlug }` öğeleriyle) — `selectFields` write operasyonunda sessizce
|
|
209
|
+
yok sayılır.
|
|
210
|
+
|
|
211
|
+
> **Gateway table step'lerinde `path`/`sort`/`limit` eksiklerini otomatik dolduruyor**
|
|
212
|
+
> (yanıtta `gatewayNormalized`). Dolu değerlere dokunmaz; yanlış path yine WF/MS block'una takılır.
|
|
213
|
+
|
|
214
|
+
## Response step'i gövdeyi `bodyExpression`'dan üretir
|
|
215
|
+
|
|
216
|
+
`rule: { dataTransferObjectId }` **hiçbir şey döndürmez** (`result: null`). Çalışan şekil:
|
|
217
|
+
|
|
218
|
+
```jsonc
|
|
219
|
+
"rule": {
|
|
220
|
+
"queryStepSourceType": "library", "isExpression": false,
|
|
221
|
+
"bodyExpression": "{\n success: true,\n message: 'Kayıt oluşturuldu.',\n id: new_enrollment.data.id\n}",
|
|
222
|
+
"httpStatusCode": 200, "headerExpression": "{ }",
|
|
223
|
+
"items": [], "returnType": "grouped", "enableDynamicFilters": false,
|
|
224
|
+
"selectFields": [], "fields": [], "returnFields": [], "filters": [], "joins": []
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Önceki step'in çıktısına **step adı + `.data`** ile erişilir (`existing_enrollment.data[0].id`,
|
|
229
|
+
`new_enrollment.data.id`) — bu yüzden step adları geçerli JavaScript tanımlayıcısı olmalı.
|
|
230
|
+
`returnDtoId` yine zorunludur (WF-002); sözleşmeyi o tanımlar, gövdeyi `bodyExpression` üretir.
|
|
231
|
+
|
|
232
|
+
## `DtoId` verilen yerde `Behavior` de zorunlu
|
|
233
|
+
|
|
234
|
+
`returnDtoId` verildiyse `returnBehavior`, `parameterDtoId` verildiyse `parameterBehavior`
|
|
235
|
+
zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → ReturnBehavior
|
|
236
|
+
.NotEmpty()`); atlamak `'Return Behavior' must not be empty` ile ilk denemeyi kırar. Değerler:
|
|
237
|
+
`object` (varsayılan tercih) · `array` (akış DİZİ döndürüyorsa) · `primitive` (DTO primitive ise
|
|
238
|
+
— aksi hâlde `WrongDtoBehaviorException`). **Gateway boş bırakılanı `object` ile doldurup
|
|
239
|
+
`gatewayNormalized` ile bildirir**; dizi/primitive dönen akışlarda değeri sen ver.
|
|
240
|
+
|
|
241
|
+
## Asgari iskelet (id'ler gerçek GUID olacak — placeholder'ları değiştir)
|
|
242
|
+
|
|
243
|
+
```jsonc
|
|
244
|
+
{
|
|
245
|
+
"name": "Product Lookup", "slug": "product-lookup", "type": "workflow",
|
|
246
|
+
"parameterDtoId": "<girdi DTO id>", "parameterBehavior": "object",
|
|
247
|
+
"returnDtoId": "<çıktı DTO id>", "returnBehavior": "object",
|
|
248
|
+
"steps": [
|
|
249
|
+
{ "id": "<GUID-1>", "name": "Start", "type": "start",
|
|
250
|
+
"branches": [{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" }] },
|
|
251
|
+
{ "id": "<GUID-3>", "name": "load_product", "type": "table",
|
|
252
|
+
"rule": { "stepType": "table", "datasourceId": "<datasource id>", "entityId": "<entity id>",
|
|
253
|
+
"operation": "get", "isSingle": true,
|
|
254
|
+
"selectFields": [{ "alias": "<entity slug>", "fieldId": "<alan id>", "fieldSlug": "name" }],
|
|
255
|
+
"filters": [{ "fieldId": "<alan id>", "fieldSlug": "name", "operator": "eq", "value": "input.productName" }],
|
|
256
|
+
"joins": [],
|
|
257
|
+
"sort": { "name": "sortField", "type": "sortType", "source": "query-string" },
|
|
258
|
+
"limit": { "name": "limit", "offset": "offset", "source": "query-string" } },
|
|
259
|
+
"branches": [{ "id": "<GUID-4>", "name": "success", "type": "success", "nextStepId": "<GUID-5>" },
|
|
260
|
+
{ "id": "<GUID-6>", "name": "fail", "type": "fail", "nextStepId": null }] },
|
|
261
|
+
{ "id": "<GUID-5>", "name": "product_response", "type": "response",
|
|
262
|
+
"rule": { "bodyExpression": "{ name: load_product.data.name }", "httpStatusCode": 200,
|
|
263
|
+
"headerExpression": "{ }" }, "branches": [] }
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Automation gerçekleri (hr + qm projelerinde doğrulandı)
|
|
269
|
+
|
|
270
|
+
- **`returnDtoId`/`parameterDtoId` automation'da YASAKTIR** (validator: "'Return Dto Id' must
|
|
271
|
+
be empty"). `returnBehavior: "object"` verilebilir.
|
|
272
|
+
- **`state: 'unready'` automation için NORMALDİR ve tetiklemeyi etkilemez** — qm'deki çalışan
|
|
273
|
+
automation'lar da unready'dir; "Studio'dan aktifleştirme" diye bir işlem yoktur.
|
|
274
|
+
- **Tetikleme kapısı üç alandır**, üçü de `automation` nesnesinde: `entityId` (tetikleyen
|
|
275
|
+
entity), `operation` (`create`/`update`/`delete`), **`isQueue` (false=anlık, true=kuyruklu;
|
|
276
|
+
null bırakılırsa tetikleme sorgusu HİÇ eşleşmez — automation sessizce ölü kalır)**.
|
|
277
|
+
`condition` opsiyonel JavaScript ifadesidir. `isActive` diye bir alan YOKTUR (yok sayılır).
|
|
278
|
+
- `steps` hem üst seviyede hem `automation` içinde verilir (PLT-007); `response` step'i
|
|
279
|
+
kullanılamaz, hata yolu için `revert` vardır.
|
|
280
|
+
- **Tetikleyen satıra `input` ile DEĞİL `row` ile erişilir**; update'te `previousRow`, işlemi
|
|
281
|
+
yapan kullanıcı `user` altındadır (`row.title`, `previousRow.user`, `user.id`). Step
|
|
282
|
+
`fields[]` öğelerinde `source: 'body'` + `value: '<ifade>'` kullanılır.
|
|
283
|
+
- **`isQueue: false` senkron demek değildir.** Tetikleme ateşle-unut çalışır; satırı yazdıktan
|
|
284
|
+
hemen sonra sonucu okursanız automation'ın yazdığı satırı göremezsiniz. Kısa bir bekleme
|
|
285
|
+
sonrası tekrar okuyun.
|
|
286
|
+
- Hem `row_create` ucundan hem `operation: 'create'` yapan bir microservice'ten tetiklenir —
|
|
287
|
+
ikisi de aynı hook'a düşer.
|
|
288
|
+
- **Bilinen platform sınırı (P-03):** kısa aralıklı ardışık tetiklerde (özellikle `rows[]` ile
|
|
289
|
+
TOPLU `row_create`) automation yalnız ilk satır için tetiklenebiliyor — kayıp satırlar
|
|
290
|
+
`automation_histories`'te hiç görünmez. Otomasyonlu entity'lere satırları **tekil** yazın
|
|
291
|
+
(ya da sonradan `automation_trigger` ile telafi edin); toplu ekleme sonrası tetiklenmeyi
|
|
292
|
+
mutlaka sayarak doğrulayın.
|
|
293
|
+
- Doğrulama: `automation_trigger` + `automation_histories` (ya da tetikleyen satırı yazıp
|
|
294
|
+
sonucu kontrol et).
|
|
295
|
+
|
|
296
|
+
```jsonc
|
|
297
|
+
{
|
|
298
|
+
"name": "New Hire Setup", "slug": "new-hire-setup", "type": "automation",
|
|
299
|
+
"returnBehavior": "object",
|
|
300
|
+
"steps": [ ...GUID id'li steps... ],
|
|
301
|
+
"automation": {
|
|
302
|
+
"name": "New Hire Setup", "slug": "new-hire-setup", "type": "automation",
|
|
303
|
+
"entityId": "<tetikleyen entity id>",
|
|
304
|
+
"operation": "create",
|
|
305
|
+
"isQueue": false,
|
|
306
|
+
"steps": [ ...aynı steps... ]
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Sık hatalar (politika lint'inin yakaladıkları)
|
|
312
|
+
|
|
313
|
+
- Bağlanmamış step grafı (WF-001, block) · workflow'da returnDtoId eksik (WF-002, block)
|
|
314
|
+
- Yanlış tipte step (WF-005, block) · GUID olmayan step/branch id'si (WF-011, block)
|
|
315
|
+
- Automation'da `isQueue`/`operation` eksik ya da returnDtoId verilmiş (WF-010, block)
|
|
316
|
+
- `fields` ∩ `selectFields` çakışması (WF-012, warn) · join'li table'da boş selectFields (WF-007, warn)
|
|
317
|
+
- Join'li table step'inde kök `rule.alias` boş (WF-019, block — Postgres 42601'in sebebi)
|
|
318
|
+
- İlişki alanının içini okuyan ifade (WF-020, warn — join yoksa alan düz FK'dır)
|
|
319
|
+
- `set-user` adımı olmadan `user` okuyan middleware (MW-003, warn)
|
|
320
|
+
|
|
321
|
+
## Hedefli değişiklik: `workflow_patch` yollarında İNDEKS KULLANMA
|
|
322
|
+
|
|
323
|
+
Saklanan `steps` dizisinin sırası, `workflow_create`'e **gönderdiğin sıra değildir**. `/steps/5/...`
|
|
324
|
+
gibi indeks yolları bu yüzden başka adımı yazar ve yanıt yine `applied: N` der — sessiz veri
|
|
325
|
+
bozulması (crm koşumu B-4). Seçici kullan:
|
|
326
|
+
|
|
327
|
+
```jsonc
|
|
328
|
+
{ "op": "replace", "path": "/steps[id=<stepId>]/rule/expression", "value": "…" }
|
|
329
|
+
{ "op": "replace", "path": "/steps[name=setUser]/rule/joins", "value": [] }
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Gateway seçiciyi gövde platforma **gitmeden** indekse çevirir, çözülemezse isteği hiç göndermez
|
|
333
|
+
ve yanıtta `patchTargets` ile her op'un hangi adıma (ad + tip + id) indiğini söyler. Aynı seçici
|
|
334
|
+
`microservice_patch`'te de çalışır (`/sourceParameters/joins[alias=customers]/path`).
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
## Table adımının filtresi: `alias` YOKSA filtre SESSİZCE DÜŞER
|
|
339
|
+
|
|
340
|
+
Bir `table` adımının `filters[]` girdisinde **`alias` zorunludur** ve entity'nin adım içindeki
|
|
341
|
+
`alias` değeriyle birebir aynı olmalıdır. Eksikse filtre uygulanmaz; hata da çıkmaz.
|
|
342
|
+
|
|
343
|
+
`operation: 'get'` + `isSingle: true` ile sonucu **felakete dönüşür**: sorgu tablodan rastgele
|
|
344
|
+
(sıralamaya göre değişen) bir satır döndürür ve akış onu doğru kayıt sanar. Ölçüm (2026-08-18,
|
|
345
|
+
`ik` zimmet akışı): `asset_id` verilmesine rağmen her koşumda başka bir demirbaş döndü; aynı
|
|
346
|
+
kusur `operation: 'update'` filtresinde **yanlış satırı günceller**.
|
|
347
|
+
|
|
348
|
+
```jsonc
|
|
349
|
+
// ✘ filtre yok sayılır — istek 200 döner
|
|
350
|
+
{ "fieldSlug": "id", "operator": "eq", "value": "input.asset_id", "fieldId": "<id alanı>" }
|
|
351
|
+
|
|
352
|
+
// ✔ doğrusu
|
|
353
|
+
{ "alias": "assets", "fieldId": "<id alanı>", "fieldSlug": "id", "operator": "eq",
|
|
354
|
+
"source": "expression", "value": "input.asset_id",
|
|
355
|
+
"filterType": "single", "conditionType": "and", "isRequired": true, "children": [], "order": 0 }
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
`source` da **`expression`** olmalı: verilmezse `body`'ye düşer ve `value` bir ifade değil
|
|
359
|
+
**gövde alanı adı** sayılır — yine filtre kaybolur.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
const resourcesDirectory = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
export function listKnowledgeFiles(kind) {
|
|
6
|
+
try {
|
|
7
|
+
return readdirSync(join(resourcesDirectory, kind)).filter(file => file.endsWith(kind === 'guides' ? '.md' : '.json'));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function readKnowledgeFile(kind, file) {
|
|
14
|
+
return readFileSync(join(resourcesDirectory, kind, file), 'utf8');
|
|
15
|
+
}
|
|
16
|
+
export function registerKnowledgeResources(server) {
|
|
17
|
+
for (const kind of ['guides', 'examples']) {
|
|
18
|
+
const files = listKnowledgeFiles(kind);
|
|
19
|
+
const mimeType = kind === 'guides' ? 'text/markdown' : 'application/json';
|
|
20
|
+
server.registerResource(`${kind}-index`, `ngapps://${kind}/index`, {
|
|
21
|
+
title: kind === 'guides' ? 'Rehber dizini' : 'Altın örnek dizini',
|
|
22
|
+
description: kind === 'guides'
|
|
23
|
+
? 'Platform iş kuralları rehberleri; modül tool\'larını kullanmadan önce okuyun.'
|
|
24
|
+
: 'Onaylanmış tam gövde örnekleri; boş org\'da şablon kaynağı budur.',
|
|
25
|
+
mimeType: 'application/json'
|
|
26
|
+
}, async (uri) => ({
|
|
27
|
+
contents: [{
|
|
28
|
+
uri: uri.href,
|
|
29
|
+
mimeType: 'application/json',
|
|
30
|
+
text: JSON.stringify({ [kind]: files.map(file => `ngapps://${kind}/${file.replace(/\.(md|json)$/, '')}`) })
|
|
31
|
+
}]
|
|
32
|
+
}));
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const name = file.replace(/\.(md|json)$/, '');
|
|
35
|
+
server.registerResource(`${kind}-${name}`, `ngapps://${kind}/${name}`, {
|
|
36
|
+
title: name,
|
|
37
|
+
description: kind === 'guides' ? `${name} modülü iş kuralları rehberi` : `${name} altın örneği`,
|
|
38
|
+
mimeType
|
|
39
|
+
}, async (uri) => ({
|
|
40
|
+
contents: [{ uri: uri.href, mimeType, text: readKnowledgeFile(kind, file) }]
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": {
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"name": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"nullable": true
|
|
8
|
+
},
|
|
9
|
+
"description": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"nullable": true
|
|
12
|
+
},
|
|
13
|
+
"dbName": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"nullable": true
|
|
16
|
+
},
|
|
17
|
+
"type": {
|
|
18
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Datastore.Datasources.DatasourceType"
|
|
19
|
+
},
|
|
20
|
+
"host": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"nullable": true
|
|
23
|
+
},
|
|
24
|
+
"port": {
|
|
25
|
+
"type": "integer",
|
|
26
|
+
"format": "int32"
|
|
27
|
+
},
|
|
28
|
+
"properties": {
|
|
29
|
+
"nullable": true
|
|
30
|
+
},
|
|
31
|
+
"connectionOptions": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"nullable": true
|
|
36
|
+
},
|
|
37
|
+
"nullable": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"additionalProperties": false
|
|
41
|
+
},
|
|
42
|
+
"components": {
|
|
43
|
+
"schemas": {
|
|
44
|
+
"NgApps.Domain.Contexts.Datastore.Datasources.DatasourceType": {
|
|
45
|
+
"enum": [
|
|
46
|
+
"postgresql",
|
|
47
|
+
"mongodb"
|
|
48
|
+
],
|
|
49
|
+
"type": "string",
|
|
50
|
+
"format": ""
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|