Ana içeriğe geç

Özel Fonksiyonlar (Custom Functions)

Özel fonksiyonlar, vNext platformunda BFF (Backend for Frontend) API kullanımını azaltmak için tasarlanmış bileşenlerdir. Instance verileri üzerinde çalışarak diğer domain'lere veya entegre servislere uç nokta sağlarlar.

İçindekiler

  1. Genel Bakış
  2. Function Tanımı
  3. Function Özellikleri
  4. Tüketim Noktaları
  5. Sistem Fonksiyonları
  6. Kullanım Örnekleri
  7. En iyi Uygulamalar

Genel Bakış

Özel fonksiyonlar şu amaçlarla kullanılır:

  • BFF API Azaltma: Doğrudan veri erişimi sağlayarak aracı API katmanlarını azaltır
  • Veri Dönüşümü: Instance verilerini mapping ile istenilen formatta sunar
  • Task Çalıştırma: Fonksiyon çağrıldığında tanımlı task'ı çalıştırır
  • Servis Entegrasyonu: Diğer domain'lere veya harici servislere uç nokta sağlar
ipucu

Her fonksiyon bir task çalıştırabilir ve task sonucundaki veri mapping ile istenilen formatta döndürülebilir.


Function Tanımı

Temel Yapı

{
"key": "function-get-user-info",
"flow": "sys-functions",
"domain": "core",
"version": "1.0.0",
"flowVersion": "1.0.0",
"tags": [
"system",
"core",
"users",
"lookup"
],
"attributes": {
"scope": "I",
"task": {
"order": 1,
"task": {
"key": "get-user-info",
"domain": "core",
"version": "1.0.0",
"flow": "sys-tasks"
},
"mapping": {
"location": "./src/GetUserInfoMapping.csx",
"code": "<BASE64_ENCODED_MAPPING_CODE>"
}
}
}
}

Function Özellikleri

Temel Özellikler (Top-Level)

ÖzellikTipZorunluPattern / KısıtAçıklama
keystringEvet^[a-z0-9-]+$Fonksiyon için benzersiz tanımlayıcı
flowstringEvetSabit: sys-functionsFlow stream bilgisi
domainstringEvet^[a-z0-9-]+$Fonksiyonun ait olduğu domain
versionstringEvet^\d+\.\d+\.\d+Versiyon bilgisi (semantic versioning)
flowVersionstringEvet^\d+\.\d+\.\d+Flow versiyon bilgisi
tagsstring[]EvetminItems: 1Kategorilendirme ve arama için etiketler
_commentstringHayırAçıklama / yorum
attributesobjectEvetFonksiyon konfigürasyonu

Attributes Özellikleri

ÖzellikTipZorunluAçıklama
scopestringEvetFonksiyon kapsamı (I = Instance, F = Flow, D = Domain)
taskobjectKoşulluTek task tanımı. task veya onExecutionTasks'tan biri zorunlu
onExecutionTasksarrayKoşulluSıralı çalıştırılacak task'lar. task veya onExecutionTasks'tan biri zorunlu
outputobjectKoşulluÇıktı mapping betiği; onExecutionTasks tanımlıysa zorunlu. IOutputHandler uygular
labelsarrayHayırÇoklu dil etiketleri. Her öğe: label (string) + language (pattern: ^[a-z]{2}-[A-Z]{2}$)
rolesarrayHayırYetkilendirme rolleri. Her öğe: role (string) + grant (allow / deny). DENY her zaman ALLOW'u geçersiz kılar
rawResponsebooleanHayırtrue: mapped rawData doğrudan response olarak döndürülür. false (varsayılan): platform kendi pattern modeli üzerinden çıktı verir. Legacy API'lerden vnext'e geçiş senaryolarında kullanılır

Scope Değerleri

DeğerAçıklamaErişim Seviyesi
IInstanceBelirli bir instance için çalışır
FWorkflowWorkflow seviyesinde çalışır
DDomainDomain seviyesinde çalışır

Task Yapısı

{
"task": {
"order": 1,
"task": {
"key": "task-key",
"domain": "core",
"version": "1.0.0",
"flow": "sys-tasks"
},
"mapping": {
"location": "./src/MappingFile.csx",
"code": "<BASE64_ENCODED_CODE>"
}
}
}
ÖzellikTipZorunluAçıklama
orderintegerEvetTask çalışma sırası (minimum: 1)
taskobjectEvetTask referansı (explicit: key, domain, flow, version veya ref: ref)
mappingobjectEvetInput/Output dönüşüm mapping'i (aşağıdaki tablo)

Mapping Özellikleri

ÖzellikTipZorunluVarsayılanAçıklama
typestringHayırLMapping tipi: G (Global) veya L (Local). Global ise code gerekmez
locationstringHayırKod dosyası yolu (pattern: ^\.\/.*\.csx$)
codestringKoşulluMapping kodu içeriği. type = L ise zorunlu
encodingstringHayırB64Kodlama formatı: B64 (Base64) veya NAT (Native/Ham)

Çoklu task çalıştırma ve output mapping

Tek bir task yerine attributes.onExecutionTasks ile sırayla birden fazla task çalıştırılabilir. Her öğede order, task referansı ve isteğe bağlı mapping bulunur. Sonraki task'lar, aynı fonksiyon yürütmesinde önceki task çıktılarını kullanabilir.

İsteğe bağlı attributes.output, IOutputHandler uygulayan bir betiğe işaret eder. OutputHandler içinde sonuçlar context.OutputResponse üzerinden okunur (anahtarlar çalıştırılan task anahtarlarına göre, tipik olarak camelCase).

"attributes": {
"scope": "I",
"onExecutionTasks": [
{
"order": 1,
"task": {
"key": "validate-account-policies",
"domain": "core",
"flow": "sys-tasks",
"version": "1.0.0"
},
"mapping": {
"location": "./src/FunctionValidatePoliciesMapping.csx",
"code": ""
}
},
{
"order": 2,
"task": {
"key": "get-data-from-workflow",
"domain": "core",
"flow": "sys-tasks",
"version": "1.0.0"
},
"mapping": {
"location": "./src/FunctionGetInstanceDataMapping.csx",
"code": ""
}
}
],
"output": {
"location": "./src/FunctionOutputMapping.csx",
"code": ""
}
}
using System.Threading.Tasks;
using BBT.Workflow.Scripting;

public class FunctionOutputMapping : IOutputHandler
{
public Task<ScriptResponse> OutputHandler(ScriptContext context)
{
var policies = context.OutputResponse["validateAccountPolicies"].data;
var instanceData = context.OutputResponse?["getDataFromWorkflow"].data;
return Task.FromResult(new ScriptResponse
{
Key = "multi-task-function-output",
Data = new { policyValidation = policies, instanceSnapshot = instanceData }
});
}
}

Tüketim Noktaları

Domain Seviyesi Fonksiyonlar

Tüm domain instance ve verilerini döndürür:

GET /api/v1/{domain}/functions

Belirli bir fonksiyonun sonucunu döndürür:

GET /api/v1/{domain}/functions/{function}
POST /api/v1/{domain}/functions/{function}
PATCH /api/v1/{domain}/functions/{function}
DELETE /api/v1/{domain}/functions/{function}

Instance Seviyesi Fonksiyonlar

Belirli bir instance için fonksiyonu çalıştırır:

GET /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/{function}
POST /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/{function}
PATCH /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/{function}
DELETE /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/{function}

:::info BFF Maliyeti POST, PATCH ve DELETE verb desteği, function endpoint'lerinin tam CRUD operasyonlarını karşılamasına olanak tanır. Bu sayede ayrı BFF katmanı geliştirme ihtiyacı minimize edilir. :::


Sistem Fonksiyonları

vNext platformu, her workflow instance'ı için hazır sistem fonksiyonları sağlar:

State Function

Instance'ın mevcut durum bilgisini döndürür.

Endpoint:

GET /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/state

Response:

{
"data": {
"href": "/core/workflows/account-opening/instances/d4b161a8-7705-4bfb-9ba4-d76461bb35eb/functions/data?extensions=extension-user-session"
},
"view": {
"loadData": true,
"href": "/core/workflows/account-opening/instances/d4b161a8-7705-4bfb-9ba4-d76461bb35eb/functions/view"
},
"state": "account-type-selection",
"status": "A",
"activeCorrelations": [],
"transitions": [
{
"name": "select-demand-deposit",
"href": "/core/workflows/account-opening/instances/d4b161a8-7705-4bfb-9ba4-d76461bb35eb/transitions/select-demand-deposit"
},
{
"name": "execute-sub",
"href": "/core/workflows/account-opening/instances/d4b161a8-7705-4bfb-9ba4-d76461bb35eb/transitions/execute-sub"
}
],
"eTag": "01KCHWT3QQFM6J9QQD9G4T0VRP"
}

Response Alanları:

AlanTipAçıklama
data.hrefstringData fonksiyon endpoint'i
view.loadDatabooleanView'ın data yüklemesi gerekip gerekmediği
view.hrefstringView fonksiyon endpoint'i
statestringMevcut state adı
statusstringInstance durumu (A=Active, C=Completed)
activeCorrelationsarrayAktif alt korelasyonlar
transitionsarrayKullanılabilir transition'lar
eTagstringCache kontrolü için ETag değeri

View Function

Instance'ın mevcut state veya transition için view verisini döndürür.

Endpoint:

GET /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/view?transitionKey={transition}&platform={platform}

Query Parametreleri:

ParametreTipAçıklama
transitionKeystringBelirli transition için view (opsiyonel)
platformstringHedef platform: web, ios, android

Response:

{
"key": "account-type-selection-view",
"content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Choose Your Account Type\",\"tr-TR\":\"Hesap Türünüzü Seçin\"},\"fields\":[...]}",
"type": "Json",
"display": "full-page",
"label": ""
}

Response Alanları:

AlanTipAçıklama
keystringView tanımlayıcısı
contentstringView içeriği (JSON formatında)
typestringİçerik tipi (Json, Html, vb.)
displaystringGösterim modu (full-page, popup, bottom-sheet, vb.)
labelstringLokalize edilmiş etiket

Schema Function

Instance'ın mevcut state veya transition için schema verisini döndürür.

Endpoint:

GET /api/v1/{domain}/workflows/{workflow}/instances/{instance}/functions/schema?transitionKey={transition}

Response:

{
"key": "account-type-selection",
"type": "workflow",
"schema": {
"$id": "https://schemas.vnext.com/banking/account-type-selection.json",
"type": "object",
"title": "Account Type Selection Schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": ["accountType"],
"properties": {
"accountType": {
"type": "string",
"oneOf": [
{
"const": "demand-deposit",
"description": "Vadesiz Hesap - Demand Deposit Account"
},
{
"const": "time-deposit",
"description": "Vadeli Hesap - Time Deposit Account"
},
{
"const": "investment-account",
"description": "Fonlu Hesap - Investment Account"
},
{
"const": "savings-account",
"description": "Tasarruf Hesabı - Savings Account"
}
],
"title": "Account Type",
"description": "Type of account to be opened"
}
},
"description": "Schema for account type selection input",
"additionalProperties": false
}
}

Kullanım Örnekleri

Örnek 1: Kullanıcı Bilgisi Fonksiyonu

{
"key": "function-get-user-info",
"flow": "sys-functions",
"domain": "core",
"version": "1.0.0",
"flowVersion": "1.0.0",
"tags": ["system", "core", "users", "lookup"],
"attributes": {
"scope": "I",
"task": {
"order": 1,
"task": {
"key": "get-user-info",
"domain": "core",
"version": "1.0.0",
"flow": "sys-tasks"
},
"mapping": {
"location": "./src/GetUserInfoMapping.csx",
"code": "<BASE64>"
}
}
}
}

Mapping Örneği:

using System.Threading.Tasks;
using BBT.Workflow.Scripting;
using BBT.Workflow.Definitions;

public class GetUserInfoMapping : IMapping
{
public Task<ScriptResponse> InputHandler(WorkflowTask task, ScriptContext context)
{
try
{
var httpTask = task as HttpTask;
if (httpTask == null)
throw new InvalidOperationException("Task must be an HttpTask");

var userId = context.Body?.userId;

// URL'yi userId ile güncelle
httpTask.SetUrl(httpTask.Url.Replace("{userId}", userId?.ToString() ?? ""));

// Header'ları ayarla
var headers = new Dictionary<string, string?>
{
["Content-Type"] = "application/json",
["Accept"] = "application/json",
["X-Request-Id"] = Guid.NewGuid().ToString()
};

httpTask.SetHeaders(headers);

return Task.FromResult(new ScriptResponse());
}
catch (Exception ex)
{
return Task.FromResult(new ScriptResponse
{
Key = "user-info-error",
Data = new { error = ex.Message }
});
}
}

public async Task<ScriptResponse> OutputHandler(ScriptContext context)
{
try
{
var statusCode = context.Body?.statusCode ?? 500;
var responseData = context.Body?.data;

if (statusCode >= 200 && statusCode < 300)
{
return new ScriptResponse
{
Key = "user-info-success",
Data = new
{
user = responseData,
phoneNumber = responseData?.phoneNumber,
hasRegisteredDevices = ((object[])responseData?.registeredDevices).Length > 0,
language = responseData?.language ?? "tr-TR"
},
Tags = new[] { "users", "lookup", "success" }
};
}
else
{
return new ScriptResponse
{
Key = "user-info-failure",
Data = new
{
error = "Failed to get user information",
errorCode = "user_info_failed",
statusCode = statusCode,
hasRegisteredDevices = false
},
Tags = new[] { "users", "lookup", "failure" }
};
}
}
catch (Exception ex)
{
return new ScriptResponse
{
Key = "user-info-exception",
Data = new
{
error = "Internal processing error",
errorCode = "processing_error",
errorDescription = ex.Message,
hasRegisteredDevices = false
},
Tags = new[] { "users", "lookup", "error" }
};
}
}
}

Örnek 2: Hesap Bakiyesi Fonksiyonu

{
"key": "function-get-account-balance",
"flow": "sys-functions",
"domain": "banking",
"version": "1.0.0",
"flowVersion": "1.0.0",
"tags": ["banking", "accounts", "balance"],
"attributes": {
"scope": "I",
"task": {
"order": 1,
"task": {
"key": "get-balance",
"domain": "banking",
"version": "1.0.0",
"flow": "sys-tasks"
},
"mapping": {
"location": "./src/GetBalanceMapping.csx",
"code": "<BASE64>"
}
}
}
}

En iyi Uygulamalar

1. Fonksiyon Tasarımı

UygulamaAçıklama
Tek sorumlulukHer fonksiyon tek bir iş yapmalı
Anlamlı isimlendirmefunction- prefix'i ile başlayan açıklayıcı isimler
Uygun scopeİhtiyaca göre doğru scope seçimi (I, W, D)
Versiyon yönetimiSemantic versioning kullanımı

2. Mapping Yazımı

UygulamaAçıklama
Hata yönetimiTry-catch blokları ile hata yakalama
Null kontrolüNull-safe kod yazımı (?. operatörü)
LoglamaUygun log mesajları ekleme
PerformansGereksiz işlemlerden kaçınma

3. Güvenlik

UygulamaAçıklama
YetkilendirmeUygun authorization kontrolleri
Veri doğrulamaInput validation yapılması
Hassas veriHassas verilerin maskelenmesi
Rate limitingİstek limitleri uygulanması

4. Performans

UygulamaAçıklama
CachingUygun cache stratejisi kullanımı
Async işlemlerAsenkron operasyonlar için async/await
TimeoutUygun timeout değerleri belirleme
Resource yönetimiKaynakların düzgün serbest bırakılması

İlgili Dökümanlar