1428 lines
70 KiB
Plaintext
1428 lines
70 KiB
Plaintext
---
|
||
title: Twenty Uygulamaları
|
||
description: Twenty özelleştirmelerini kod olarak oluşturun ve yönetin.
|
||
---
|
||
|
||
<Warning>
|
||
Uygulamalar şu anda alfa testinde. Özellik işlevsel ancak hâlâ gelişmekte.
|
||
</Warning>
|
||
|
||
## Uygulamalar Nedir?
|
||
|
||
Uygulamalar, Twenty özelleştirmelerini **kod olarak** oluşturup yönetmenizi sağlar. Her şeyi UI üzerinden yapılandırmak yerine, veri modelinizi ve mantık fonksiyonlarınızı kodla tanımlarsınız — bu da oluşturmayı, bakımı ve birden çok çalışma alanına dağıtmayı hızlandırır.
|
||
|
||
**Bugün Yapabilecekleriniz:**
|
||
|
||
* Özel nesneleri ve alanları kod olarak tanımlayın (yönetilen veri modeli)
|
||
* Özel tetikleyicilerle mantık fonksiyonları oluşturun
|
||
* Yapay zekâ için yetenekleri ve ajanları tanımlayın
|
||
* Aynı uygulamayı birden çok çalışma alanına dağıtın
|
||
|
||
## Ön Gereksinimler
|
||
|
||
* Node.js 24+ ve Yarn 4
|
||
* Docker (yerel Twenty geliştirme sunucusu için)
|
||
|
||
## Başlarken
|
||
|
||
Resmi iskelet oluşturucusunu kullanarak yeni bir uygulama oluşturun. Sizin için otomatik olarak yerel bir Twenty örneğini başlatabilir:
|
||
|
||
```bash filename="Terminal"
|
||
# Yeni bir uygulamanın iskeletini oluşturun — CLI yerel bir Twenty sunucusunu başlatmayı önerecektir
|
||
npx create-twenty-app@latest my-twenty-app
|
||
cd my-twenty-app
|
||
|
||
# Geliştirme modunu başlatın: yerel değişiklikleri çalışma alanınızla otomatik olarak senkronize eder
|
||
yarn twenty dev
|
||
```
|
||
|
||
### Yerel Sunucu Yönetimi
|
||
|
||
SDK, yerel bir Twenty geliştirme sunucusunu yönetmek için komutlar içerir (PostgreSQL, Redis, sunucu ve worker içeren hepsi bir arada Docker imajı):
|
||
|
||
```bash filename="Terminal"
|
||
# Yerel sunucuyu başlatın (gerekirse imajı indirir)
|
||
yarn twenty server start
|
||
|
||
# Sunucu durumunu kontrol edin
|
||
yarn twenty server status
|
||
|
||
# Sunucu günlüklerini akış olarak görüntüleyin
|
||
yarn twenty server logs
|
||
|
||
# Sunucuyu durdurun
|
||
yarn twenty server stop
|
||
|
||
# Tüm verileri sıfırlayın ve temiz bir başlangıç yapın
|
||
yarn twenty server reset
|
||
```
|
||
|
||
Yerel sunucu, bir çalışma alanı ve kullanıcıyla (`tim@apple.dev` / `tim@apple.dev`) önceden yapılandırılmış olarak gelir; böylece herhangi bir manuel kurulum gerektirmeden hemen geliştirmeye başlayabilirsiniz.
|
||
|
||
### Kimlik Doğrulama
|
||
|
||
Uygulamanızı OAuth kullanarak yerel sunucuya bağlayın:
|
||
|
||
```bash filename="Terminal"
|
||
# Authenticate via OAuth (opens browser)
|
||
yarn twenty remote add --local
|
||
```
|
||
|
||
İskelet oluşturucu, hangi örnek dosyaların dahil edileceğini kontrol etmek için iki modu destekler:
|
||
|
||
```bash filename="Terminal"
|
||
# Varsayılan (kapsamlı): tüm örnekler (nesne, alan, mantık fonksiyonu, ön bileşen, görünüm, gezinme menüsü öğesi, yetenek, ajan)
|
||
npx create-twenty-app@latest my-app
|
||
|
||
# Minimal: yalnızca çekirdek dosyalar (application-config.ts ve default-role.ts)
|
||
npx create-twenty-app@latest my-app --minimal
|
||
```
|
||
|
||
### Yerel bir Twenty örneği nasıl kullanılır?
|
||
|
||
Zaten yerel olarak bir Twenty örneği çalıştırıyorsanız, Docker kullanmak yerine ona bağlanabilirsiniz. Yerel sunucunuzun dinlediği bağlantı noktasını belirtin (varsayılan: `3000`):
|
||
|
||
```bash filename="Terminal"
|
||
# During scaffolding
|
||
npx create-twenty-app@latest my-app --port 3000
|
||
|
||
# Or after scaffolding
|
||
yarn twenty remote add --local --port 3000
|
||
```
|
||
|
||
Buradan şunları yapabilirsiniz:
|
||
|
||
```bash filename="Terminal"
|
||
# Add a new entity to your application (guided)
|
||
yarn twenty entity:add
|
||
|
||
# Watch your application's function logs
|
||
yarn twenty function:logs
|
||
|
||
# Execute a function by name
|
||
yarn twenty function:execute -n my-function -p '{\"name\": \"test\"}'
|
||
|
||
# Execute the pre-install function
|
||
yarn twenty function:execute --preInstall
|
||
|
||
# Execute the post-install function
|
||
yarn twenty function:execute --postInstall
|
||
|
||
# Build the app for distribution
|
||
yarn twenty build
|
||
|
||
# Publish the app to npm or a Twenty server
|
||
yarn twenty publish
|
||
|
||
# Uninstall the application from the current workspace
|
||
yarn twenty uninstall
|
||
|
||
# Display commands' help
|
||
yarn twenty help},{
|
||
```
|
||
|
||
Ayrıca bkz.: [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) ve [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk) için CLI başvuru sayfaları.
|
||
|
||
## Proje yapısı (şablondan oluşturulmuş)
|
||
|
||
`npx create-twenty-app@latest my-twenty-app` komutunu çalıştırdığınızda scaffolder şunları yapar:
|
||
|
||
* Minimal bir temel uygulamayı `my-twenty-app/` içine kopyalar
|
||
* Yerel bir `twenty-sdk` bağımlılığı ve Yarn 4 yapılandırması ekler
|
||
* `twenty` CLI ile bağlantılı yapılandırma dosyaları ve betikler oluşturur
|
||
* İskelet oluşturma moduna bağlı olarak çekirdek dosyaları (uygulama yapılandırması, varsayılan işlev rolü, kurulum öncesi ve kurulum sonrası işlevler) ile örnek dosyaları üretir
|
||
|
||
Varsayılan `--exhaustive` moduyla yeni oluşturulmuş bir uygulama şu şekilde görünür:
|
||
|
||
```text filename="my-twenty-app/"
|
||
my-twenty-app/
|
||
package.json
|
||
yarn.lock
|
||
.gitignore
|
||
.nvmrc
|
||
.yarnrc.yml
|
||
.yarn/
|
||
install-state.gz
|
||
.oxlintrc.json
|
||
tsconfig.json
|
||
README.md
|
||
public/ # Genel varlıklar klasörü (görseller, yazı tipleri vb.)
|
||
src/
|
||
├── application-config.ts # Gerekli - ana uygulama yapılandırması
|
||
├── roles/
|
||
│ └── default-role.ts # Mantık fonksiyonları için varsayılan rol
|
||
├── objects/
|
||
│ └── example-object.ts # Örnek özel nesne tanımı
|
||
├── fields/
|
||
│ └── example-field.ts # Örnek bağımsız alan tanımı
|
||
├── logic-functions/
|
||
│ ├── hello-world.ts # Örnek mantık fonksiyonu
|
||
│ ├── pre-install.ts # Kurulum öncesi mantık fonksiyonu
|
||
│ └── post-install.ts # Kurulum sonrası mantık fonksiyonu
|
||
├── front-components/
|
||
│ └── hello-world.tsx # Örnek ön bileşen
|
||
├── views/
|
||
│ └── example-view.ts # Örnek kaydedilmiş görünüm tanımı
|
||
├── navigation-menu-items/
|
||
│ └── example-navigation-menu-item.ts # Örnek kenar çubuğu gezinme bağlantısı
|
||
├── skills/
|
||
│ └── example-skill.ts # Örnek yapay zekâ ajanı yetenek tanımı
|
||
└── agents/
|
||
└── example-agent.ts # Örnek yapay zekâ ajanı tanımı
|
||
```
|
||
|
||
`--minimal` ile yalnızca çekirdek dosyalar oluşturulur (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` ve `logic-functions/post-install.ts`).
|
||
|
||
Genel hatlarıyla:
|
||
|
||
* **package.json**: Uygulama adını, sürümünü, motorları (Node 24+, Yarn 4) bildirir ve `twenty-sdk` ile yerel `twenty` CLI'sine yetki devreden bir `twenty` betiği ekler. Tüm mevcut komutları listelemek için `yarn twenty help` komutunu çalıştırın.
|
||
* **.gitignore**: `node_modules`, `.yarn`, `generated/` (türlendirilmiş istemci), `dist/`, `build/`, kapsam klasörleri, günlük dosyaları ve `.env*` dosyaları gibi yaygın artifaktları yok sayar.
|
||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Proje tarafından kullanılan Yarn 4 araç zincirini kilitler ve yapılandırır.
|
||
* **.nvmrc**: Projenin beklediği Node.js sürümünü sabitler.
|
||
* **.oxlintrc.json** ve **tsconfig.json**: Uygulamanızın TypeScript kaynakları için linting ve TypeScript yapılandırması sağlar.
|
||
* **README.md**: Uygulama kökünde temel talimatların yer aldığı kısa bir README.
|
||
* **public/**: Uygulamanızla birlikte sunulacak genel varlıkları (görseller, yazı tipleri, statik dosyalar) depolamak için bir klasör. Buraya yerleştirilen dosyalar senkronizasyon sırasında yüklenir ve çalışma zamanında erişilebilir olur.
|
||
* **src/**: Uygulamanızı kod olarak tanımladığınız ana yer
|
||
|
||
### Varlık algılama
|
||
|
||
SDK, TypeScript dosyalarınızı **`export default define<Entity>({...})`** çağrılarını arayarak ayrıştırıp varlıkları algılar. Her varlık türünün, `twenty-sdk` tarafından dışa aktarılan karşılık gelen bir yardımcı fonksiyonu vardır:
|
||
|
||
| Yardımcı fonksiyon | Varlık türü |
|
||
| ---------------------------------- | -------------------------------------------------------- |
|
||
| `defineObject()` | Özel nesne tanımları |
|
||
| `defineLogicFunction()` | Mantık fonksiyon tanımları |
|
||
| `definePreInstallLogicFunction()` | Kurulum öncesi mantık işlevi (kurulumdan önce çalışır) |
|
||
| `definePostInstallLogicFunction()` | Kurulum sonrası mantık işlevi (kurulumdan sonra çalışır) |
|
||
| `defineFrontComponent()` | Front component definitions |
|
||
| `defineRole()` | Rol tanımları |
|
||
| `defineField()` | Mevcut nesneler için alan genişletmeleri |
|
||
| `defineView()` | Kaydedilmiş görünüm tanımları |
|
||
| `defineNavigationMenuItem()` | Gezinme menüsü öğesi tanımları |
|
||
| `defineSkill()` | Yapay zekâ ajanı yetenek tanımları |
|
||
| `defineAgent()` | Yapay zekâ ajanı tanımları |
|
||
|
||
<Note>
|
||
**Dosya adlandırma esnektir.** Varlık algılama AST tabanlıdır — SDK, kaynak dosyalarınızı `export default define<Entity>({...})` desenini bulmak için tarar. Dosyalarınızı ve klasörlerinizi dilediğiniz gibi düzenleyebilirsiniz. Varlık türüne göre gruplama (örn. `logic-functions/`, `roles/`) bir gereklilik değil, yalnızca kod organizasyonu için bir gelenektir.
|
||
</Note>
|
||
|
||
Algılanan bir varlığa örnek:
|
||
|
||
```typescript
|
||
// This file can be named anything and placed anywhere in src/
|
||
import { defineObject, FieldType } from 'twenty-sdk';
|
||
|
||
export default defineObject({
|
||
universalIdentifier: '...',
|
||
nameSingular: 'postCard',
|
||
// ... rest of config
|
||
});
|
||
```
|
||
|
||
İlerideki komutlar daha fazla dosya ve klasör ekleyecektir:
|
||
|
||
* `yarn twenty dev`, `node_modules/twenty-sdk/clients` içinde iki tiplendirilmiş API istemcisini otomatik olarak oluşturur: `CoreApiClient` (`/graphql` üzerinden çalışma alanı verileri için) ve `MetadataApiClient` (çalışma alanı yapılandırması ve `/metadata` üzerinden dosya yüklemeleri için).
|
||
* `yarn twenty entity:add`, özel nesneleriniz, fonksiyonlarınız, ön bileşenleriniz, rolleriniz, yetenekleriniz ve daha fazlası için `src/` altında varlık tanım dosyaları ekler.
|
||
|
||
## Kimlik Doğrulama
|
||
|
||
`yarn twenty auth:login` komutunu ilk kez çalıştırdığınızda, sizden şunlar istenir:
|
||
|
||
* API URL’si (varsayılan: http://localhost:3000 veya mevcut çalışma alanı profiliniz)
|
||
* API anahtarı
|
||
|
||
Kimlik bilgileriniz kullanıcı başına `~/.twenty/config.json` içinde saklanır. You can maintain multiple profiles and switch between them.
|
||
|
||
### Managing workspaces
|
||
|
||
```bash filename="Terminal"
|
||
# Etkileşimli giriş yapın (önerilir)
|
||
yarn twenty auth:login
|
||
|
||
# Belirli bir çalışma alanı profiline giriş yapın
|
||
yarn twenty auth:login --workspace my-custom-workspace
|
||
|
||
# Yapılandırılmış tüm çalışma alanlarını listeleyin
|
||
yarn twenty auth:list
|
||
|
||
# Varsayılan çalışma alanını değiştirin (etkileşimli)
|
||
yarn twenty auth:switch
|
||
|
||
# Belirli bir çalışma alanına geçin
|
||
yarn twenty auth:switch production
|
||
|
||
# Mevcut kimlik doğrulama durumunu kontrol edin
|
||
yarn twenty auth:status
|
||
```
|
||
|
||
`yarn twenty auth:switch` ile çalışma alanlarını değiştirdikten sonra, sonraki tüm komutlar varsayılan olarak o çalışma alanını kullanacaktır. You can still override it temporarily with `--workspace <name>`.
|
||
|
||
## SDK kaynaklarını kullanın (türler ve yapılandırma)
|
||
|
||
twenty-sdk, uygulamanız içinde kullandığınız türlendirilmiş yapı taşları ve yardımcı fonksiyonlar sağlar. Aşağıda en sık dokunacağınız başlıca parçalar yer alıyor.
|
||
|
||
### Yardımcı fonksiyonlar
|
||
|
||
SDK, uygulama varlıklarınızı tanımlamak için yardımcı fonksiyonlar sağlar. [Varlık algılama](#entity-detection) bölümünde açıklandığı gibi, varlıklarınızın algılanması için `export default define<Entity>({...})` kullanmalısınız:
|
||
|
||
| Fonksiyon | Amaç |
|
||
| ---------------------------------- | ------------------------------------------------------------------------- |
|
||
| `defineApplication()` | Uygulama meta verilerini yapılandırın (zorunlu, uygulama başına bir adet) |
|
||
| `defineObject()` | Alanlara sahip özel nesneler tanımlayın |
|
||
| `defineLogicFunction()` | İşleyicilerle mantık fonksiyonları tanımlayın |
|
||
| `definePreInstallLogicFunction()` | Bir kurulum öncesi mantık işlevi tanımlayın (uygulama başına bir adet) |
|
||
| `definePostInstallLogicFunction()` | Bir kurulum sonrası mantık işlevi tanımlayın (uygulama başına bir adet) |
|
||
| `defineFrontComponent()` | Özel kullanıcı arayüzü için ön uç bileşenlerini tanımlayın |
|
||
| `defineRole()` | Rol izinlerini ve nesne erişimini yapılandırın |
|
||
| `defineField()` | Mevcut nesneleri ek alanlarla genişletin |
|
||
| `defineView()` | Nesneler için kaydedilmiş görünümler tanımlayın |
|
||
| `defineNavigationMenuItem()` | Kenar çubuğu gezinme bağlantılarını tanımlayın |
|
||
| `defineSkill()` | Yapay zekâ ajanı yeteneklerini tanımlayın |
|
||
| `defineAgent()` | Sistem istemleriyle yapay zekâ ajanları tanımlayın |
|
||
|
||
Bu fonksiyonlar, derleme zamanında yapılandırmanızı doğrular ve IDE otomatik tamamlama ile tür güvenliği sağlar.
|
||
|
||
### Nesnelerin tanımlanması
|
||
|
||
Özel nesneler, çalışma alanınızdaki kayıtlar için hem şemayı hem de davranışı tanımlar. Yerleşik doğrulamayla nesneler tanımlamak için `defineObject()` kullanın:
|
||
|
||
```typescript
|
||
// src/app/postCard.object.ts
|
||
import { defineObject, FieldType } from 'twenty-sdk';
|
||
|
||
enum PostCardStatus {
|
||
DRAFT = 'DRAFT',
|
||
SENT = 'SENT',
|
||
DELIVERED = 'DELIVERED',
|
||
RETURNED = 'RETURNED',
|
||
}
|
||
|
||
export default defineObject({
|
||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||
nameSingular: 'postCard',
|
||
namePlural: 'postCards',
|
||
labelSingular: 'Post Card',
|
||
labelPlural: 'Post Cards',
|
||
description: 'A post card object',
|
||
icon: 'IconMail',
|
||
fields: [
|
||
{
|
||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||
name: 'content',
|
||
type: FieldType.TEXT,
|
||
label: 'Content',
|
||
description: "Postcard's content",
|
||
icon: 'IconAbc',
|
||
},
|
||
{
|
||
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
|
||
name: 'recipientName',
|
||
type: FieldType.FULL_NAME,
|
||
label: 'Recipient name',
|
||
icon: 'IconUser',
|
||
},
|
||
{
|
||
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
|
||
name: 'recipientAddress',
|
||
type: FieldType.ADDRESS,
|
||
label: 'Recipient address',
|
||
icon: 'IconHome',
|
||
},
|
||
{
|
||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||
name: 'status',
|
||
type: FieldType.SELECT,
|
||
label: 'Status',
|
||
icon: 'IconSend',
|
||
defaultValue: `'${PostCardStatus.DRAFT}'`,
|
||
options: [
|
||
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
|
||
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
|
||
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
|
||
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
|
||
],
|
||
},
|
||
{
|
||
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
|
||
name: 'deliveredAt',
|
||
type: FieldType.DATE_TIME,
|
||
label: 'Delivered at',
|
||
icon: 'IconCheck',
|
||
isNullable: true,
|
||
defaultValue: null,
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* Yerleşik doğrulama ve daha iyi IDE desteği için `defineObject()` kullanın.
|
||
* `universalIdentifier` dağıtımlar arasında benzersiz ve kararlı olmalıdır.
|
||
* Her alan bir `name`, `type`, `label` ve kendi kararlı `universalIdentifier` değerini gerektirir.
|
||
* `fields` dizisi isteğe bağlıdır — özel alanlar olmadan da nesneler tanımlayabilirsiniz.
|
||
* `yarn twenty entity:add` kullanarak, adlandırma, alanlar ve ilişkiler konusunda sizi yönlendirerek yeni nesneler oluşturabilirsiniz.
|
||
|
||
<Note>
|
||
**Temel alanlar otomatik olarak oluşturulur.** Özel bir nesne tanımladığınızda Twenty, standart alanları otomatik olarak ekler
|
||
örneğin `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` ve `deletedAt`.
|
||
Bunları `fields` dizinizde tanımlamanız gerekmez — yalnızca özel alanlarınızı ekleyin.
|
||
`fields` dizinizde aynı ada sahip bir alan tanımlayarak varsayılan alanları geçersiz kılabilirsiniz,
|
||
ancak bu önerilmez.
|
||
</Note>
|
||
|
||
### Mevcut nesneler üzerinde alanları tanımlama
|
||
|
||
Mevcut nesnelere özel alanlar eklemek için `defineField()` kullanın — hem standart nesnelere (ör. `company`, `person`, `opportunity`) hem de diğer uygulamalar tarafından tanımlanan özel nesnelere. Her alan kendi dosyasında bulunur ve hedef nesneye `universalIdentifier` ile başvurur.
|
||
|
||
Standart nesnelere başvurmak için `twenty-sdk` içinden `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` öğesini içe aktarın. Bu sabit, tüm yerleşik nesneler ve onların alanları için kararlı tanımlayıcılar sağlar:
|
||
|
||
```typescript
|
||
// src/fields/apollo-total-funding.field.ts
|
||
import {
|
||
defineField,
|
||
FieldType,
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||
} from 'twenty-sdk';
|
||
|
||
export default defineField({
|
||
universalIdentifier: 'c90ae72d-4ddf-4f22-882f-eef98c91e40e',
|
||
objectUniversalIdentifier:
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||
type: FieldType.CURRENCY,
|
||
name: 'apolloTotalFunding',
|
||
label: 'Total Funding',
|
||
description: 'Total funding raised by the company',
|
||
icon: 'IconCash',
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* `objectUniversalIdentifier`, alanın hangi nesneye ekleneceğini Twenty'ye bildirir. `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.<objectName>.universalIdentifier` standart nesneler için kullanın.
|
||
* Her alan için kararlı bir `universalIdentifier`, `name`, `type`, `label` ve hedef `objectUniversalIdentifier` gerekir.
|
||
* `yarn twenty entity:add` kullanarak yeni alanlar oluşturabilir ve alan seçeneğini seçebilirsiniz.
|
||
* `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`, kolaylık olması için `STANDARD_OBJECT` olarak da dışa aktarılır — her ikisi de aynı sabite atıfta bulunur.
|
||
|
||
Kullanılabilir standart nesneler şunları içerir: `attachment`, `blocklist`, `calendarChannel`, `calendarEvent`, `calendarEventParticipant`, `company`, `connectedAccount`, `dashboard`, `favorite`, `favoriteFolder`, `message`, `messageChannel`, `messageParticipant`, `messageThread`, `note`, `noteTarget`, `opportunity`, `person`, `task`, `taskTarget`, `timelineActivity`, `workflow`, `workflowAutomatedTrigger`, `workflowRun`, `workflowVersion` ve `workspaceMember`.
|
||
|
||
Her standart nesne ayrıca alan tanımlayıcılarını da sunar. Örneğin, rol izinlerinde standart bir nesnedeki belirli bir alana başvurmak için:
|
||
|
||
```typescript
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name.universalIdentifier
|
||
```
|
||
|
||
#### Mevcut nesnelerde ilişki alanları
|
||
|
||
Mevcut nesneleri özel nesnelerinize bağlayan ilişki alanlarını da tanımlayabilirsiniz:
|
||
|
||
```typescript
|
||
// src/fields/people-on-call-recording.field.ts
|
||
import { defineField, FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk';
|
||
import { CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/call-recording';
|
||
import { CALL_RECORDING_ON_PERSON_ID } from 'src/fields/call-recording-on-person.field';
|
||
|
||
export default defineField({
|
||
universalIdentifier: '4a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d',
|
||
objectUniversalIdentifier:
|
||
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
|
||
type: FieldType.RELATION,
|
||
name: 'person',
|
||
label: 'Person',
|
||
relationTargetObjectMetadataUniversalIdentifier:
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
|
||
relationTargetFieldMetadataUniversalIdentifier:
|
||
CALL_RECORDING_ON_PERSON_ID,
|
||
relationType: RelationType.MANY_TO_ONE,
|
||
});
|
||
```
|
||
|
||
### Uygulama yapılandırması (application-config.ts)
|
||
|
||
Her uygulamanın aşağıdakileri açıklayan tek bir `application-config.ts` dosyası vardır:
|
||
|
||
* **Uygulamanın kim olduğu**: tanımlayıcılar, görünen ad ve açıklama.
|
||
* **Fonksiyonlarının nasıl çalıştığı**: izinler için hangi rolü kullandıkları.
|
||
* **(İsteğe bağlı) değişkenler**: fonksiyonlarınıza ortam değişkenleri olarak sunulan anahtar–değer çiftleri.
|
||
* **(İsteğe bağlı) kurulum öncesi işlev**: uygulama yüklenmeden önce çalışan bir mantık işlevi.
|
||
* **(İsteğe bağlı) kurulum sonrası işlev**: uygulama yüklendikten sonra çalışan bir mantık işlevi.
|
||
|
||
Uygulama yapılandırmanızı tanımlamak için `defineApplication()` kullanın:
|
||
|
||
```typescript
|
||
// src/application-config.ts
|
||
import { defineApplication } from 'twenty-sdk';
|
||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
||
|
||
export default defineApplication({
|
||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||
displayName: 'My Twenty App',
|
||
description: 'My first Twenty app',
|
||
icon: 'IconWorld',
|
||
applicationVariables: {
|
||
DEFAULT_RECIPIENT_NAME: {
|
||
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
||
description: 'Default recipient name for postcards',
|
||
value: 'Jane Doe',
|
||
isSecret: false,
|
||
},
|
||
},
|
||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||
});
|
||
```
|
||
|
||
Notlar:
|
||
|
||
* `universalIdentifier` alanları size ait belirleyici kimliklerdir; bunları bir kez oluşturun ve eşitlemeler boyunca kararlı tutun.
|
||
* `applicationVariables`, fonksiyonlarınız için ortam değişkenlerine dönüşür (örneğin, `DEFAULT_RECIPIENT_NAME` değeri `process.env.DEFAULT_RECIPIENT_NAME` olarak kullanılabilir).
|
||
* `defaultRoleUniversalIdentifier`, rol dosyasıyla eşleşmelidir (aşağıya bakın).
|
||
* Kurulum öncesi ve kurulum sonrası işlevler, manifest oluşturma sırasında otomatik olarak algılanır. Bkz. [Kurulum öncesi işlevler](#pre-install-functions) ve [Kurulum sonrası işlevler](#post-install-functions).
|
||
|
||
#### Roller ve izinler
|
||
|
||
Uygulamalar, çalışma alanınızdaki nesneler ve eylemler üzerindeki izinleri kapsülleyen roller tanımlayabilir. `application-config.ts` içindeki `defaultRoleUniversalIdentifier` alanı, uygulamanızın mantık fonksiyonlarının kullandığı varsayılan rolü belirtir.
|
||
|
||
* `TWENTY_API_KEY` olarak enjekte edilen çalışma zamanı API anahtarı bu varsayılan fonksiyon rolünden türetilir.
|
||
* Türlendirilmiş istemci, o role tanınan izinlerle sınırlandırılır.
|
||
* En az ayrıcalık ilkesini izleyin: Yalnızca fonksiyonlarınızın ihtiyaç duyduğu izinlere sahip özel bir rol oluşturun ve ardından evrensel tanımlayıcısına referans verin.
|
||
|
||
##### Varsayılan fonksiyon rolü (\*.role.ts)
|
||
|
||
Yeni bir uygulama oluşturduğunuzda CLI ayrıca varsayılan bir rol dosyası da oluşturur. Yerleşik doğrulamayla roller tanımlamak için `defineRole()` kullanın:
|
||
|
||
```typescript
|
||
// src/roles/default-role.ts
|
||
import { defineRole, PermissionFlag } from 'twenty-sdk';
|
||
|
||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||
'b648f87b-1d26-4961-b974-0908fd991061';
|
||
|
||
export default defineRole({
|
||
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||
label: 'Default function role',
|
||
description: 'Default role for function Twenty client',
|
||
canReadAllObjectRecords: false,
|
||
canUpdateAllObjectRecords: false,
|
||
canSoftDeleteAllObjectRecords: false,
|
||
canDestroyAllObjectRecords: false,
|
||
canUpdateAllSettings: false,
|
||
canBeAssignedToAgents: false,
|
||
canBeAssignedToUsers: false,
|
||
canBeAssignedToApiKeys: false,
|
||
objectPermissions: [
|
||
{
|
||
objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
|
||
canReadObjectRecords: true,
|
||
canUpdateObjectRecords: true,
|
||
canSoftDeleteObjectRecords: false,
|
||
canDestroyObjectRecords: false,
|
||
},
|
||
],
|
||
fieldPermissions: [
|
||
{
|
||
objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
|
||
fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff',
|
||
canReadFieldValue: false,
|
||
canUpdateFieldValue: false,
|
||
},
|
||
],
|
||
permissionFlags: [PermissionFlag.APPLICATIONS],
|
||
});
|
||
```
|
||
|
||
Bu rolün `universalIdentifier` değeri daha sonra `application-config.ts` içinde `defaultRoleUniversalIdentifier` olarak referans verilir. Başka bir deyişle:
|
||
|
||
* **\*.role.ts**, varsayılan fonksiyon rolünün neler yapabileceğini tanımlar.
|
||
* **application-config.ts**, fonksiyonlarınızın izinlerini devralması için bu role işaret eder.
|
||
|
||
Notlar:
|
||
|
||
* Oluşturulan rol ile başlayın ve en az ayrıcalık ilkesini izleyerek aşamalı olarak kısıtlayın.
|
||
* `objectPermissions` ve `fieldPermissions` değerlerini, fonksiyonlarınızın ihtiyaç duyduğu nesneler/alanlarla değiştirin.
|
||
* `permissionFlags`, platform düzeyindeki yeteneklere erişimi kontrol eder. Minimumda tutun; yalnızca ihtiyacınız olanları ekleyin.
|
||
* Çalışan bir örneği Hello World uygulamasında görün: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||
|
||
### Mantık fonksiyon yapılandırması ve giriş noktası
|
||
|
||
Her fonksiyon dosyası, bir işleyici ve isteğe bağlı tetikleyiciler içeren bir yapılandırmayı dışa aktarmak için `defineLogicFunction()` kullanır.
|
||
|
||
```typescript
|
||
// src/app/createPostCard.logic-function.ts
|
||
import { defineLogicFunction } from 'twenty-sdk';
|
||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||
import { CoreApiClient, type Person } from 'twenty-client-sdk/core';
|
||
|
||
const handler = async (params: RoutePayload) => {
|
||
const client = new CoreApiClient();
|
||
const name = 'name' in params.queryStringParameters
|
||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Merhaba dünya'
|
||
: 'Merhaba dünya';
|
||
|
||
const result = await client.mutation({
|
||
createPostCard: {
|
||
__args: { data: { name } },
|
||
id: true,
|
||
name: true,
|
||
},
|
||
});
|
||
return result;
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||
name: 'create-new-post-card',
|
||
timeoutSeconds: 2,
|
||
handler,
|
||
triggers: [
|
||
// Herkese açık HTTP rota tetikleyicisi '/s/post-card/create'
|
||
{
|
||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||
type: 'route',
|
||
path: '/post-card/create',
|
||
httpMethod: 'GET',
|
||
isAuthRequired: false,
|
||
},
|
||
// Cron tetikleyicisi (CRON deseni)
|
||
// {
|
||
// universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
|
||
// type: 'cron',
|
||
// pattern: '0 0 1 1 *',
|
||
// },
|
||
// Veritabanı olay tetikleyicisi
|
||
// {
|
||
// universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
|
||
// type: 'databaseEvent',
|
||
// eventName: 'person.updated',
|
||
// updatedFields: ['name'],
|
||
// },
|
||
],
|
||
});
|
||
```
|
||
|
||
Yaygın tetikleyici türleri:
|
||
|
||
* **route**: Fonksiyonunuzu bir HTTP yolu ve yöntemiyle **`/s/` uç noktası altında** sunar:
|
||
|
||
> örn. `path: '/post-card/create',` -> `<APP_URL>/s/post-card/create` üzerinden çağırın
|
||
|
||
* **cron**: Bir CRON ifadesi kullanarak fonksiyonunuzu bir zamanlamayla çalıştırır.
|
||
* **databaseEvent**: Çalışma alanı nesnesi yaşam döngüsü olaylarında çalışır. Olay işlemi `updated` olduğunda, dinlenecek belirli alanlar `updatedFields` dizisinde belirtilebilir. Tanımsız veya boş bırakılırsa, herhangi bir güncelleme fonksiyonu tetikler.
|
||
|
||
> örn. `person.updated`
|
||
|
||
Notlar:
|
||
|
||
* `triggers` dizisi isteğe bağlıdır. Tetikleyicisi olmayan fonksiyonlar, diğer fonksiyonlar tarafından çağrılan yardımcı fonksiyonlar olarak kullanılabilir.
|
||
* Tek bir fonksiyonda birden çok tetikleyici türünü birleştirebilirsiniz.
|
||
|
||
### Kurulum öncesi işlevler
|
||
|
||
Kurulum öncesi işlev, uygulamanız bir çalışma alanına yüklenmeden önce otomatik olarak çalışan bir mantık işlevidir. Bu, doğrulama görevleri, önkoşul kontrolleri veya ana kurulum başlamadan önce çalışma alanı durumunun hazırlanması için yararlıdır.
|
||
|
||
`create-twenty-app` ile yeni bir uygulama iskeleti oluşturduğunuzda, `src/logic-functions/pre-install.ts` konumunda sizin için bir kurulum öncesi işlev oluşturulur:
|
||
|
||
```typescript
|
||
// src/logic-functions/pre-install.ts
|
||
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||
|
||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||
};
|
||
|
||
export default definePreInstallLogicFunction({
|
||
universalIdentifier: '<generated-uuid>',
|
||
name: 'pre-install',
|
||
description: 'Runs before installation to prepare the application.',
|
||
timeoutSeconds: 300,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
Ayrıca kurulum öncesi işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz:
|
||
|
||
```bash filename="Terminal"
|
||
yarn twenty function:execute --preInstall
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* Kurulum öncesi işlevler `definePreInstallLogicFunction()` kullanır — tetikleyici ayarlarını atlayan (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`) özel bir varyanttır.
|
||
* İşleyici, `{ previousVersion: string }` içeren bir `InstallLogicFunctionPayload` alır — daha önce yüklü olan uygulamanın sürümü (veya yeni kurulumlar için boş bir dize).
|
||
* Uygulama başına yalnızca bir kurulum öncesi işlevine izin verilir. Birden fazla tespit edilirse manifest oluşturma hataya düşer.
|
||
* İşlevin `universalIdentifier` değeri, oluşturma sırasında uygulama manifestinde otomatik olarak `preInstallLogicFunctionUniversalIdentifier` olarak ayarlanır — `defineApplication()` içinde buna atıfta bulunmanıza gerek yoktur.
|
||
* Varsayılan zaman aşımı, daha uzun hazırlık görevlerine izin vermek için 300 saniye (5 dakika) olarak ayarlanmıştır.
|
||
* Kurulum öncesi işlevlerin tetikleyicilere ihtiyacı yoktur — kurulumdan önce platform tarafından veya `function:execute --preInstall` aracılığıyla manuel olarak çağrılırlar.
|
||
|
||
### Kurulum sonrası işlevler
|
||
|
||
Kurulum sonrası işlev, uygulamanız bir çalışma alanına yüklendikten sonra otomatik olarak çalışan bir mantık işlevidir. Bu, varsayılan verileri tohumlama, ilk kayıtları oluşturma veya çalışma alanı ayarlarını yapılandırma gibi tek seferlik kurulum görevleri için yararlıdır.
|
||
|
||
`create-twenty-app` ile yeni bir uygulama iskeleti oluşturduğunuzda, `src/logic-functions/post-install.ts` konumunda sizin için bir kurulum sonrası işlevi oluşturulur:
|
||
|
||
```typescript
|
||
// src/logic-functions/post-install.ts
|
||
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||
|
||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||
};
|
||
|
||
export default definePostInstallLogicFunction({
|
||
universalIdentifier: '<generated-uuid>',
|
||
name: 'post-install',
|
||
description: 'Runs after installation to set up the application.',
|
||
timeoutSeconds: 300,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
Ayrıca kurulum sonrası işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz:
|
||
|
||
```bash filename="Terminal"
|
||
yarn twenty function:execute --postInstall
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* Kurulum sonrası işlevler `definePostInstallLogicFunction()` kullanır — tetikleyici ayarlarını atlayan (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`) özel bir varyanttır.
|
||
* İşleyici, `{ previousVersion: string }` içeren bir `InstallLogicFunctionPayload` alır — daha önce yüklü olan uygulamanın sürümü (veya yeni kurulumlar için boş bir dize).
|
||
* Uygulama başına yalnızca bir kurulum sonrası işlevine izin verilir. Birden fazla tespit edilirse manifest oluşturma hataya düşer.
|
||
* İşlevin `universalIdentifier` değeri, oluşturma sırasında uygulama manifestinde otomatik olarak `postInstallLogicFunctionUniversalIdentifier` olarak ayarlanır — `defineApplication()` içinde buna atıfta bulunmanıza gerek yoktur.
|
||
* Varsayılan zaman aşımı, veri tohumlama gibi daha uzun kurulum görevlerine izin vermek için 300 saniye (5 dakika) olarak ayarlanmıştır.
|
||
* Kurulum sonrası işlevlerin tetikleyicilere ihtiyacı yoktur — kurulum sırasında platform tarafından veya `function:execute --postInstall` aracılığıyla manuel olarak çağrılırlar.
|
||
|
||
### Rota tetikleyicisi yükü
|
||
|
||
<Warning>
|
||
**Kırıcı değişiklik (v1.16, Ocak 2026):** Rota tetikleyicisi yük formatı değişti. v1.16'dan önce, sorgu parametreleri, yol parametreleri ve gövde doğrudan payload olarak gönderiliyordu. v1.16 itibarıyla, yapılandırılmış bir `RoutePayload` nesnesinin içine yerleştiriliyorlar.
|
||
|
||
**v1.16'dan önce:**
|
||
```typescript
|
||
const handler = async (params) => {
|
||
const { param1, param2 } = params; // Direct access
|
||
};
|
||
```
|
||
|
||
**v1.16'dan sonra:**
|
||
```typescript
|
||
const handler = async (event: RoutePayload) => {
|
||
const { param1, param2 } = event.body; // Access via .body
|
||
const { queryParam } = event.queryStringParameters;
|
||
const { id } = event.pathParameters;
|
||
};
|
||
```
|
||
|
||
**Mevcut fonksiyonları taşımak için:** İşleyicinizi, parametreler nesnesinden doğrudan ayırmak yerine `event.body`, `event.queryStringParameters` veya `event.pathParameters` üzerinden ayrıştıracak şekilde güncelleyin.
|
||
</Warning>
|
||
|
||
Bir rota tetikleyicisi mantık fonksiyonunuzu çağırdığında, AWS HTTP API v2 formatını izleyen bir `RoutePayload` nesnesi alır. Türü `twenty-sdk` içinden içe aktarın:
|
||
|
||
```typescript
|
||
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk';
|
||
|
||
const handler = async (event: RoutePayload) => {
|
||
// Access request data
|
||
const { headers, queryStringParameters, pathParameters, body } = event;
|
||
|
||
// HTTP method and path are available in requestContext
|
||
const { method, path } = event.requestContext.http;
|
||
|
||
return { message: 'Success' };
|
||
};
|
||
```
|
||
|
||
`RoutePayload` türünün yapısı şu şekildedir:
|
||
|
||
| Özellik | Tür | Açıklama |
|
||
| ---------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------- |
|
||
| `headers` | `Record<string, string \| undefined>` | HTTP başlıkları (`forwardedRequestHeaders` içinde listelenenlerle sınırlı) |
|
||
| `queryStringParameters` | `Record<string, string \| undefined>` | Sorgu dizesi parametreleri (birden çok değer virgülle birleştirilir) |
|
||
| `pathParameters` | `Record<string, string \| undefined>` | Rota deseninden çıkarılan yol parametreleri (örn., `/users/:id` → `{ id: '123' }`) |
|
||
| `gövde` | `object \| null` | Ayrıştırılmış istek gövdesi (JSON) |
|
||
| `isBase64Encoded` | `boolean` | Gövdenin base64 ile kodlanıp kodlanmadığı |
|
||
| `requestContext.http.method` | `string` | HTTP yöntemi (GET, POST, PUT, PATCH, DELETE) |
|
||
| `requestContext.http.path` | `string` | Ham istek yolu |
|
||
|
||
### HTTP başlıklarını iletme
|
||
|
||
Varsayılan olarak, güvenlik nedenleriyle gelen isteklerden HTTP başlıkları mantık fonksiyonunuza **aktarılmaz**. Belirli başlıklara erişmek için bunları açıkça `forwardedRequestHeaders` dizisinde listeleyin:
|
||
|
||
```typescript
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||
name: 'webhook-handler',
|
||
handler,
|
||
triggers: [
|
||
{
|
||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||
type: 'route',
|
||
path: '/webhook',
|
||
httpMethod: 'POST',
|
||
isAuthRequired: false,
|
||
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
Daha sonra işleyicinizde bu başlıklara erişebilirsiniz:
|
||
|
||
```typescript
|
||
const handler = async (event: RoutePayload) => {
|
||
const signature = event.headers['x-webhook-signature'];
|
||
const contentType = event.headers['content-type'];
|
||
|
||
// Validate webhook signature...
|
||
return { received: true };
|
||
};
|
||
```
|
||
|
||
<Note>
|
||
Başlık adları küçük harfe normalize edilir. Onlara küçük harfli anahtarlarla erişin (örneğin, `event.headers['content-type']`).
|
||
</Note>
|
||
|
||
Yeni fonksiyonları iki şekilde oluşturabilirsiniz:
|
||
|
||
* **Şablondan**: `yarn twenty entity:add` çalıştırın ve yeni bir mantık fonksiyonu ekleme seçeneğini seçin. Bu, bir işleyici ve yapılandırma içeren bir başlangıç dosyası oluşturur.
|
||
* **Manuel**: Yeni bir `*.logic-function.ts` dosyası oluşturun ve aynı deseni izleyerek `defineLogicFunction()` kullanın.
|
||
|
||
### Bir mantık işlevini araç olarak işaretleme
|
||
|
||
Mantık işlevleri, yapay zeka ajanları ve iş akışları için **araçlar** olarak sunulabilir. Bir işlev bir araç olarak işaretlendiğinde, Twenty'nin yapay zeka özellikleri tarafından keşfedilebilir hâle gelir ve iş akışı otomasyonlarında bir adım olarak seçilebilir.
|
||
|
||
Bir mantık işlevini bir araç olarak işaretlemek için `isTool: true` olarak ayarlayın ve beklenen giriş parametrelerini açıklayan bir `toolInputSchema`yı [JSON Şeması](https://json-schema.org/) kullanarak sağlayın:
|
||
|
||
```typescript
|
||
// src/logic-functions/enrich-company.logic-function.ts
|
||
import { defineLogicFunction } from 'twenty-sdk';
|
||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||
|
||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||
const client = new CoreApiClient();
|
||
|
||
const result = await client.mutation({
|
||
createTask: {
|
||
__args: {
|
||
data: {
|
||
title: `${params.companyName} için verileri zenginleştir`,
|
||
body: `Alan adı: ${params.domain ?? 'bilinmiyor'}`,
|
||
},
|
||
},
|
||
id: true,
|
||
},
|
||
});
|
||
|
||
return { taskId: result.createTask.id };
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
||
name: 'enrich-company',
|
||
description: 'Bir şirket kaydını harici verilerle zenginleştir',
|
||
timeoutSeconds: 10,
|
||
handler,
|
||
isTool: true,
|
||
toolInputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
companyName: {
|
||
type: 'string',
|
||
description: 'Zenginleştirilecek şirketin adı',
|
||
},
|
||
domain: {
|
||
type: 'string',
|
||
description: 'Şirket web sitesi alan adı (isteğe bağlı)',
|
||
},
|
||
},
|
||
required: ['companyName'],
|
||
},
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* **`isTool`** (`boolean`, varsayılan: `false`): `true` olarak ayarlandığında, işlev bir araç olarak kaydedilir ve AI ajanları ile iş akışı otomasyonları tarafından kullanılabilir hale gelir.
|
||
* **`toolInputSchema`** (`object`, isteğe bağlı): İşlevinizin kabul ettiği parametreleri tanımlayan bir JSON Schema nesnesi. AI ajanları, aracın hangi girdileri beklediğini anlamak ve çağrıları doğrulamak için bu şemayı kullanır. Atlanırsa, şema varsayılan olarak `{ type: 'object', properties: {} }` olur (parametre yok).
|
||
* `isTool: false` (veya ayarlanmamış) olan işlevler araç olarak **sunulmaz**. Yine de doğrudan yürütülebilir veya diğer işlevler tarafından çağrılabilirler, ancak araç keşfinde görünmezler.
|
||
* **Araç adlandırma**: Bir araç olarak sunulduğunda, işlev adı otomatik olarak `logic_function_<name>` biçimine dönüştürülür (küçük harfe çevrilir, alfasayısal olmayan karakterler alt çizgi ile değiştirilir). Örneğin, `enrich-company` `logic_function_enrich_company` haline gelir.
|
||
* `isTool` özelliğini tetikleyicilerle birleştirebilirsiniz — bir işlev aynı anda hem bir araç (AI ajanları tarafından çağrılabilir) olabilir hem de olaylar tarafından tetiklenebilir (cron, veritabanı olayları, routes).
|
||
|
||
<Note>
|
||
**İyi bir `description` yazın.** AI ajanları, aracı ne zaman kullanacaklarına karar vermek için işlevin `description` alanına güvenir. Aracın ne yaptığını ve ne zaman çağrılması gerektiğini açıkça belirtin.
|
||
</Note>
|
||
|
||
### Ön uç bileşenleri
|
||
|
||
Ön uç bileşenleri, Twenty'nin kullanıcı arayüzünde görüntülenen özel React bileşenleri oluşturmanıza olanak tanır. Yerleşik doğrulamayla bileşenleri tanımlamak için `defineFrontComponent()` kullanın:
|
||
|
||
```typescript
|
||
// src/front-components/my-widget.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
|
||
const MyWidget = () => {
|
||
return (
|
||
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
||
<h1>My Custom Widget</h1>
|
||
<p>This is a custom front component for Twenty.</p>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'my-widget',
|
||
description: 'A custom widget component',
|
||
component: MyWidget,
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* Ön uç bileşenleri, Twenty içinde yalıtılmış bağlamlarda görüntülenen React bileşenleridir.
|
||
* `component` alanı, React bileşeninize referans verir.
|
||
* Bileşenler, `yarn twenty dev` sırasında otomatik olarak oluşturulur ve senkronize edilir.
|
||
|
||
Yeni ön uç bileşenlerini iki şekilde oluşturabilirsiniz:
|
||
|
||
* **Şablondan**: `yarn twenty entity:add` çalıştırın ve yeni bir ön uç bileşeni ekleme seçeneğini seçin.
|
||
* **Manuel**: Aynı deseni izleyerek yeni bir `.tsx` dosyası oluşturun ve `defineFrontComponent()` kullanın.
|
||
|
||
#### Ön bileşenlerin kullanılabileceği yerler
|
||
|
||
Ön bileşenler, Twenty içinde iki konumda işlenebilir:
|
||
|
||
* **Yan panel** — Headless olmayan ön bileşenler, sağ taraftaki yan panelde açılır. Bir ön bileşen komut menüsünden tetiklendiğinde varsayılan davranış budur.
|
||
* **Widget'lar (panolar ve kayıt sayfaları)** — Ön bileşenler, sayfa düzenlerine widget olarak gömülebilir. Bir pano veya kayıt sayfası düzeni yapılandırılırken kullanıcılar bir ön bileşen widget'ı ekleyebilir.
|
||
|
||
#### Headless ve headless olmayan
|
||
|
||
Ön bileşenler, `isHeadless` seçeneğiyle kontrol edilen iki işleme kipiyle gelir:
|
||
|
||
**Headless olmayan (varsayılan)** — Bileşen görünür bir kullanıcı arayüzü (UI) oluşturur. Komut menüsünden tetiklendiğinde yan panelde açılır. `isHeadless` `false` olduğunda veya belirtilmediğinde bu varsayılan davranıştır.
|
||
|
||
**Headless** — Bileşen arka planda görünmez şekilde bağlanır. Yan paneli açmaz. Headless bileşenler, mantığı çalıştırıp ardından kendilerini kaldıran eylemler için tasarlanmıştır — örneğin, bir async görevi çalıştırma, bir sayfaya gitme veya bir onay modalı gösterme. Aşağıda açıklanan SDK Command bileşenleriyle doğal olarak eşleşirler.
|
||
|
||
```typescript
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'my-action',
|
||
description: 'Runs an action without opening the side panel',
|
||
component: MyAction,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
|
||
label: 'Run my action',
|
||
},
|
||
});
|
||
```
|
||
|
||
#### Komut menüsüne öğe ekleme
|
||
|
||
Bir ön bileşenin Twenty'nin komut menüsünde bir öğe olarak görünmesi için `defineFrontComponent()` içine `command` özelliğini ekleyin. Kullanıcılar komut menüsünü (Cmd+K / Ctrl+K) açtığında, öğe görüntülenir ve tıklandığında ön bileşeni tetikler.
|
||
|
||
`command` nesnesi aşağıdaki alanları kabul eder:
|
||
|
||
| Alan | Tür | Açıklama |
|
||
| --------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||
| `universalIdentifier` | `string` (zorunlu) | Komut menüsü öğesi için benzersiz kimlik |
|
||
| `etiket` | `string` (zorunlu) | Komut menüsünde gösterilen etiket |
|
||
| `simge` | `string` (isteğe bağlı) | Simge adı (ör. `'IconSparkles'`) |
|
||
| `isPinned` | `boolean` (isteğe bağlı) | Komutun menünün en üstüne sabitlenip sabitlenmediği |
|
||
| `availabilityType` | `'GLOBAL' \| 'RECORD_SELECTION'` (isteğe bağlı) | `GLOBAL` komutu her yerde gösterir; `RECORD_SELECTION` ise yalnızca kayıt bağlamlarında gösterir |
|
||
| `availabilityObjectUniversalIdentifier` | `string` (isteğe bağlı) | Komutu belirli bir nesne türüyle sınırlandırın (ör. Person) |
|
||
|
||
Person kayıtlarına özel bir komut ekleyen çağrı kaydı uygulamasından bir örnek:
|
||
|
||
```typescript
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
|
||
name: 'Summarize Person Call Recordings',
|
||
description: 'Generates a summary of call recordings for a person',
|
||
component: SummarizePersonRecordings,
|
||
command: {
|
||
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-234567890123',
|
||
label: 'Summarize call recordings',
|
||
icon: 'IconSparkles',
|
||
isPinned: false,
|
||
availabilityType: 'RECORD_SELECTION',
|
||
availabilityObjectUniversalIdentifier:
|
||
'20202020-e674-48e5-a542-72570eee7213',
|
||
},
|
||
});
|
||
```
|
||
|
||
Komut senkronize edildiğinde komut menüsünde görünür. Ön bileşen headless değilse, yan panel bileşen içeride işlenmiş halde açılır. Headless ise bileşen arka planda bağlanır ve mantığını yürütür.
|
||
|
||
#### SDK Command bileşenleri
|
||
|
||
`twenty-sdk` paketi, headless ön bileşenler için tasarlanmış dört Command yardımcı bileşeni sağlar. Her bileşen bağlandığında bir eylem yürütür, hataları bir snackbar bildirimi göstererek ele alır ve tamamlandığında ön bileşeni otomatik olarak kaldırır.
|
||
|
||
Bunları `twenty-sdk/command` içinden içe aktarın:
|
||
|
||
* **`Command`** — `execute` prop'u aracılığıyla async bir geri çağrıyı çalıştırır.
|
||
* **`CommandLink`** — Bir uygulama yoluna gider. Props: `to`, `params`, `queryParams`, `options`.
|
||
* **`CommandModal`** — Bir onay modalı açar. Kullanıcı onaylarsa `execute` geri çağrısını yürütür. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||
* **`CommandOpenSidePanelPage`** — Belirli bir yan panel sayfasını açar. Props: `page`, `pageTitle`, `pageIcon`.
|
||
|
||
`Command` kullanarak komut menüsünden bir eylem çalıştıran headless bir ön bileşenin tam örneği:
|
||
|
||
```typescript
|
||
// src/front-components/run-action.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
import { Command } from 'twenty-sdk/command';
|
||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||
|
||
const RunAction = () => {
|
||
const execute = async () => {
|
||
const client = new CoreApiClient();
|
||
|
||
await client.mutation({
|
||
createTask: {
|
||
__args: { data: { title: 'Created by my app' } },
|
||
id: true,
|
||
},
|
||
});
|
||
};
|
||
|
||
return <Command execute={execute} />;
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||
name: 'run-action',
|
||
description: 'Creates a task from the command menu',
|
||
component: RunAction,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||
label: 'Run my action',
|
||
icon: 'IconPlayerPlay',
|
||
},
|
||
});
|
||
```
|
||
|
||
Ve yürütmeden önce onay istemek için `CommandModal` kullanan bir örnek:
|
||
|
||
```typescript
|
||
// src/front-components/delete-draft.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
import { CommandModal } from 'twenty-sdk/command';
|
||
|
||
const DeleteDraft = () => {
|
||
const execute = async () => {
|
||
// perform the deletion
|
||
};
|
||
|
||
return (
|
||
<CommandModal
|
||
title="Delete draft?"
|
||
subtitle="This action cannot be undone."
|
||
execute={execute}
|
||
confirmButtonText="Delete"
|
||
confirmButtonAccent="danger"
|
||
/>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456',
|
||
name: 'delete-draft',
|
||
description: 'Deletes a draft with confirmation',
|
||
component: DeleteDraft,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567',
|
||
label: 'Delete draft',
|
||
icon: 'IconTrash',
|
||
},
|
||
});
|
||
```
|
||
|
||
#### Yürütme bağlamı
|
||
|
||
Her ön bileşen, nerede ve nasıl çalıştığına dair bilgi sağlayan bir yürütme bağlamı alır. Bağlam değerlerine `twenty-sdk` içindeki hook'ları kullanarak erişin:
|
||
|
||
| Hook | Dönüş türü | Açıklama |
|
||
| ----------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `useFrontComponentId()` | `string` | Geçerli ön bileşen örneğinin benzersiz kimliği |
|
||
| `useRecordId()` | `string \| null` | Bileşen bir kayıt bağlamında çalıştığında (ör. bir kayıt sayfası widget'ı veya bir kayda özel bir komut) geçerli kaydın kimliği. Aksi halde `null` döner. |
|
||
| `useUserId()` | `string \| null` | Geçerli kullanıcının kimliği |
|
||
|
||
```typescript
|
||
import { useRecordId, useUserId } from 'twenty-sdk';
|
||
|
||
const MyWidget = () => {
|
||
const recordId = useRecordId();
|
||
const userId = useUserId();
|
||
|
||
return (
|
||
<div>
|
||
<p>Record: {recordId ?? 'none'}</p>
|
||
<p>User: {userId ?? 'anonymous'}</p>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
Bağlam tepkiseldir — çevredeki kayıt değişirse, hook'lar güncellenmiş değerleri otomatik olarak döndürür.
|
||
|
||
#### Host API işlevleri
|
||
|
||
Ön bileşenler yalıtılmış bir korumalı alanda çalışır ancak host tarafından sağlanan bir dizi işleve aracılığıyla Twenty'nin arayüzüyle etkileşime girebilir. Bunları doğrudan `twenty-sdk` içinden içe aktarın:
|
||
|
||
```typescript
|
||
import {
|
||
navigate,
|
||
closeSidePanel,
|
||
enqueueSnackbar,
|
||
unmountFrontComponent,
|
||
openSidePanelPage,
|
||
openCommandConfirmationModal,
|
||
} from 'twenty-sdk';
|
||
```
|
||
|
||
| Fonksiyon | İmza | Açıklama |
|
||
| ------------------------------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `gezin` | `(to, params?, queryParams?, options?) => Promise<void>` | Twenty içinde tiplendirilmiş bir uygulama yoluna gidin |
|
||
| `closeSidePanel` | `() => Promise<void>` | Yan paneli kapat |
|
||
| `enqueueSnackbar` | `(params) => Promise<void>` | Bir snackbar bildirimi gösterin. Parametreler: `message`, `variant` (`'error'`, `'success'`, `'info'`, `'warning'`), isteğe bağlı `duration`, `detailedMessage`, `dedupeKey` |
|
||
| `unmountFrontComponent` | `() => Promise<void>` | Geçerli ön bileşeni kaldırın (yürütmeden sonra temizlemek için headless bileşenler tarafından kullanılır) |
|
||
| `openSidePanelPage` | `(params) => Promise<void>` | Yan panelde bir sayfa açın. Parametreler: `page`, `pageTitle`, `pageIcon`, `shouldResetSearchState` |
|
||
| `openCommandConfirmationModal` | `(params) => Promise<'confirm' \| 'cancel'>` | Bir onay modalı gösterin ve kullanıcının yanıtını bekleyin. Parametreler: `title`, `subtitle`, `confirmButtonText`, `confirmButtonAccent` (`'default'`, `'blue'`, `'danger'`) |
|
||
|
||
Bir eylem tamamlandıktan sonra bir snackbar göstermek ve yan paneli kapatmak için host API'sini kullanan bir örnek:
|
||
|
||
```typescript
|
||
import { defineFrontComponent, useRecordId } from 'twenty-sdk';
|
||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk';
|
||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||
|
||
const ArchiveRecord = () => {
|
||
const recordId = useRecordId();
|
||
|
||
const handleArchive = async () => {
|
||
const client = new CoreApiClient();
|
||
|
||
await client.mutation({
|
||
updateTask: {
|
||
__args: { id: recordId, data: { status: 'ARCHIVED' } },
|
||
id: true,
|
||
},
|
||
});
|
||
|
||
await enqueueSnackbar({
|
||
message: 'Record archived',
|
||
variant: 'success',
|
||
});
|
||
|
||
await closeSidePanel();
|
||
};
|
||
|
||
return (
|
||
<div style={{ padding: '20px' }}>
|
||
<p>Archive this record?</p>
|
||
<button onClick={handleArchive}>Archive</button>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678',
|
||
name: 'archive-record',
|
||
description: 'Archives the current record',
|
||
component: ArchiveRecord,
|
||
});
|
||
```
|
||
|
||
### Beceriler
|
||
|
||
Yetenekler, yapay zekâ ajanlarının çalışma alanınızda kullanabileceği yeniden kullanılabilir yönergeleri ve kabiliyetleri tanımlar. Yerleşik doğrulamayla yetenekleri tanımlamak için `defineSkill()` kullanın:
|
||
|
||
```typescript
|
||
// src/skills/example-skill.ts
|
||
import { defineSkill } from 'twenty-sdk';
|
||
|
||
export default defineSkill({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'sales-outreach',
|
||
label: 'Sales Outreach',
|
||
description: 'Guides the AI agent through a structured sales outreach process',
|
||
icon: 'IconBrain',
|
||
content: `You are a sales outreach assistant. When reaching out to a prospect:
|
||
1. Research the company and recent news
|
||
2. Identify the prospect's role and likely pain points
|
||
3. Draft a personalized message referencing specific details
|
||
4. Keep the tone professional but conversational`,
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* `name`, yetenek için benzersiz bir tanımlayıcı dizedir (kebab-case önerilir).
|
||
* `label`, UI'de gösterilen, insan tarafından okunabilir addır.
|
||
* `content`, yetenek yönergelerini içerir — bu, yapay zekâ ajanının kullandığı metindir.
|
||
* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar.
|
||
* `description` (isteğe bağlı), yeteneğin amacı hakkında ek bağlam sağlar.
|
||
|
||
Yeni yetenekleri iki şekilde oluşturabilirsiniz:
|
||
|
||
* **Şablondan**: `yarn twenty entity:add` komutunu çalıştırın ve yeni bir yetenek ekleme seçeneğini seçin.
|
||
* **Manuel**: Yeni bir dosya oluşturun ve aynı deseni izleyerek `defineSkill()` kullanın.
|
||
|
||
### Temsilciler
|
||
|
||
Ajanlar, çalışma alanınızda çalışabilen sistem istemlerine sahip yapay zekâ ajanlarını tanımlar. Yerleşik doğrulamayla ajanları tanımlamak için `defineAgent()` kullanın:
|
||
|
||
```typescript
|
||
// src/agents/example-agent.ts
|
||
import { defineAgent } from 'twenty-sdk';
|
||
|
||
export default defineAgent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'sales-assistant',
|
||
label: 'Sales Assistant',
|
||
description: 'An AI agent that helps with sales tasks',
|
||
icon: 'IconRobot',
|
||
prompt: `You are a sales assistant. Help users with:
|
||
1. Researching prospects and companies
|
||
2. Drafting personalized outreach messages
|
||
3. Tracking follow-ups and next steps
|
||
4. Analyzing deal pipeline and suggesting actions`,
|
||
});
|
||
```
|
||
|
||
Önemli noktalar:
|
||
|
||
* `name`, ajan için benzersiz bir tanımlayıcı dizedir (kebab-case önerilir).
|
||
* `label`, UI'de gösterilen, insan tarafından okunabilir addır.
|
||
* `prompt`, sistem istemini içerir — bu, ajanın davranışını tanımlayan talimat metnidir.
|
||
* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar.
|
||
* `description` (isteğe bağlı), ajanın amacı hakkında ek bağlam sağlar.
|
||
|
||
Yeni ajanları iki şekilde oluşturabilirsiniz:
|
||
|
||
* **Şablondan**: `yarn twenty entity:add` komutunu çalıştırın ve yeni bir ajan ekleme seçeneğini seçin.
|
||
* **Manuel**: Yeni bir dosya oluşturun ve aynı deseni izleyerek `defineAgent()` kullanın.
|
||
|
||
### Oluşturulan tiplendirilmiş istemciler
|
||
|
||
Çalışma alanı şemanıza göre `yarn twenty dev` tarafından iki tiplendirilmiş istemci otomatik olarak oluşturulur ve `node_modules/twenty-sdk/clients` içine kaydedilir:
|
||
|
||
* **`CoreApiClient`** — çalışma alanı verileri için `/graphql` uç noktasını sorgular
|
||
* **`MetadataApiClient`** — çalışma alanı yapılandırması ve dosya yüklemeleri için `/metadata` uç noktasını sorgular.
|
||
|
||
```typescript
|
||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||
|
||
const client = new CoreApiClient();
|
||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||
|
||
const metadataClient = new MetadataApiClient();
|
||
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });},{
|
||
```
|
||
|
||
`CoreApiClient`, nesneleriniz veya alanlarınız değiştiğinde `yarn twenty dev` tarafından otomatik olarak yeniden oluşturulur. `MetadataApiClient`, SDK ile birlikte önceden hazırlanmış olarak gelir.
|
||
|
||
#### Mantık fonksiyonlarında çalışma zamanı kimlik bilgileri
|
||
|
||
Fonksiyonunuz Twenty üzerinde çalıştığında, platform kodunuz yürütülmeden önce kimlik bilgilerini ortam değişkenleri olarak enjekte eder:
|
||
|
||
* `TWENTY_API_URL`: Uygulamanızın hedeflediği Twenty API'nin temel URL’si.
|
||
* `TWENTY_API_KEY`: Uygulamanızın varsayılan fonksiyon rolü kapsamına sahip kısa ömürlü anahtar.
|
||
|
||
Notlar:
|
||
|
||
* Oluşturulan istemciye URL veya API anahtarı geçirmeniz gerekmez. Çalışma zamanında `TWENTY_API_URL` ve `TWENTY_API_KEY` değerlerini process.env üzerinden okur.
|
||
* API anahtarının izinleri, `application-config.ts` içinde `defaultRoleUniversalIdentifier` aracılığıyla referans verilen role göre belirlenir. Bu, uygulamanızın mantık fonksiyonları tarafından kullanılan varsayılan roldür.
|
||
* Uygulamalar, en az ayrıcalık ilkesini izlemek için roller tanımlayabilir. Yalnızca fonksiyonlarınızın ihtiyaç duyduğu izinleri verin ve ardından `defaultRoleUniversalIdentifier` değerini o rolün evrensel tanımlayıcısına yönlendirin.
|
||
|
||
#### Dosya yükleme
|
||
|
||
`MetadataApiClient`, çalışma alanı nesnelerinizdeki dosya türündeki alanlara dosya eklemek için bir `uploadFile` yöntemi içerir. Standart GraphQL istemcileri çok parçalı dosya yüklemelerini yerel olarak desteklemediğinden, istemci arka planda [GraphQL çok parçalı istek belirtimi](https://github.com/jaydenseric/graphql-multipart-request-spec) uygulayan bu özel yöntemi sağlar.
|
||
|
||
```typescript
|
||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||
import * as fs from 'fs';
|
||
|
||
const metadataClient = new MetadataApiClient();
|
||
|
||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||
|
||
const uploadedFile = await metadataClient.uploadFile(
|
||
fileBuffer, // dosya içeriği (Buffer olarak)
|
||
'invoice.pdf', // dosya adı
|
||
'application/pdf', // MIME türü (varsayılan: 'application/octet-stream')
|
||
'58a0a314-d7ea-4865-9850-7fb84e72f30b', // alanın evrensel tanımlayıcısı
|
||
);
|
||
|
||
console.log(uploadedFile);
|
||
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
|
||
```
|
||
|
||
Yöntem imzası:
|
||
|
||
```typescript
|
||
uploadFile(
|
||
fileBuffer: Buffer,
|
||
filename: string,
|
||
contentType: string,
|
||
fieldMetadataUniversalIdentifier: string,
|
||
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
|
||
```
|
||
|
||
| Parametre | Tür | Açıklama |
|
||
| ---------------------------------- | -------- | ------------------------------------------------------------------------------------------ |
|
||
| `fileBuffer` | `Buffer` | Dosyanın ham içeriği |
|
||
| `filename` | `string` | Dosyanın adı (depolama ve görüntüleme için kullanılır) |
|
||
| `contentType` | `string` | Dosyanın MIME türü (belirtilmezse varsayılan olarak `application/octet-stream` kullanılır) |
|
||
| `fieldMetadataUniversalIdentifier` | `string` | Nesnenizdeki dosya türü alanının `universalIdentifier` değeri |
|
||
|
||
Önemli noktalar:
|
||
|
||
* `uploadFile` yöntemi, yükleme mutasyonu `/metadata` uç noktası tarafından çözümlendiği için `MetadataApiClient` üzerinde mevcuttur.
|
||
* Alan için `universalIdentifier` kullanılır (çalışma alanına özgü kimliği değil), böylece yükleme kodunuz uygulamanızın yüklü olduğu herhangi bir çalışma alanında çalışır — uygulamaların başka her yerde alanlara nasıl atıfta bulunduğuyla tutarlıdır.
|
||
* Döndürülen `url`, yüklenen dosyaya erişmek için kullanabileceğiniz imzalı bir URL'dir.
|
||
|
||
### Hello World örneği
|
||
|
||
Nesneleri, mantık fonksiyonlarını, ön uç bileşenlerini ve birden çok tetikleyiciyi gösteren minimal, uçtan uca bir örneği [buradan](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world) inceleyin:
|
||
|
||
## Uygulamanızı derleme
|
||
|
||
Uygulamanızı `app:dev` ile geliştirdikten sonra, `app:build` kullanarak onu dağıtılabilir bir pakete derleyin.
|
||
|
||
```bash filename="Terminal"
|
||
# Build the app (output goes to .twenty/output/)
|
||
yarn twenty build
|
||
|
||
# Build and create a tarball (.tgz) for distribution
|
||
yarn twenty build --tarball
|
||
```
|
||
|
||
Derleme süreci:
|
||
|
||
1. **Manifesti ayrıştırır ve doğrular** — kaynak dosyalarınızdaki tüm `defineX()` varlıklarını okur ve manifest yapısını doğrular.
|
||
2. **Mantık işlevlerini ve ön bileşenleri derler** — TypeScript kaynaklarını esbuild kullanarak ESM `.mjs` dosyalarına paketler.
|
||
3. **Sağlama toplamları üretir** — her bir oluşturulan dosya için MD5 karmalarını hesaplar ve manifestte `builtHandlerChecksum` / `builtComponentChecksum` olarak saklar.
|
||
4. **Tipli API istemcisini oluşturur** — GraphQL şemasını inceleyip tipli `CoreApiClient` ve `MetadataApiClient` istemcilerini üretir.
|
||
5. **TypeScript tip denetimi çalıştırır** — yayımlamadan önce tip hatalarını yakalamak için `tsc --noEmit` çalıştırır.
|
||
6. **Oluşturulan istemciyle yeniden derler** — oluşturulan istemci tiplerinin dahil edilmesi için ikinci bir derleme geçişi yapar.
|
||
7. **İsteğe bağlı olarak bir tarball oluşturur** — `--tarball` iletilirse, dağıtıma hazır bir `.tgz` dosyası oluşturmak için `npm pack` çalıştırır.
|
||
|
||
`.twenty/output/` içindeki derleme çıktısı şunları içerir:
|
||
|
||
```text
|
||
.twenty/output/
|
||
├── manifest.json # Manifest with checksums for all built files
|
||
├── package.json # Copied from app root
|
||
├── yarn.lock # Copied from app root
|
||
├── src/
|
||
│ ├── logic-functions/ # Compiled .mjs logic function files
|
||
│ └── front-components/ # Compiled .mjs front component files
|
||
├── public/ # Static assets (if any)
|
||
└── my-app-1.0.0.tgz # Only with --tarball flag
|
||
```
|
||
|
||
| Seçenek | Açıklama |
|
||
| ----------- | --------------------------------------------------------- |
|
||
| `[appPath]` | Uygulama dizininin yolu (varsayılan olarak geçerli dizin) |
|
||
| `--tarball` | Çıktıyı ayrıca bir `.tgz` tarball olarak paketler |
|
||
|
||
## Uygulamanızı yayımlama
|
||
|
||
Uygulamanızı dağıtmak için `app:publish` komutunu kullanın — npm kayıt defterine ya da doğrudan bir Twenty sunucusuna yayımlayın.
|
||
|
||
### npm'ye yayımlama (varsayılan)
|
||
|
||
```bash filename="Terminal"
|
||
# Publish to npm (requires npm login)
|
||
yarn twenty publish
|
||
|
||
# Publish with a dist-tag (e.g. beta, next)
|
||
yarn twenty publish --tag beta
|
||
```
|
||
|
||
Bu, uygulamayı derler ve `.twenty/output/` dizininden `npm publish` çalıştırır. Yayımlanan paket daha sonra Twenty pazar yerinden herhangi bir çalışma alanı tarafından kurulabilir.
|
||
|
||
### Bir Twenty sunucusuna yayımlama
|
||
|
||
```bash filename="Terminal"
|
||
# Publish directly to a Twenty server
|
||
yarn twenty publish --server https://app.twenty.com
|
||
```
|
||
|
||
Bu, uygulamayı bir tarball ile derler, `uploadAppTarball` GraphQL mutasyonu aracılığıyla sunucuya yükler ve tek adımda kurulumu tetikler. Bu, özel dağıtımlar veya belirli bir sunucuya karşı test yapmak için kullanışlıdır.
|
||
|
||
| Seçenek | Açıklama |
|
||
| ----------------- | ---------------------------------------------------------------- |
|
||
| `[appPath]` | Uygulama dizininin yolu (varsayılan olarak geçerli dizin) |
|
||
| `--server <url>` | npm yerine bir Twenty sunucusuna yayımlar |
|
||
| `--token <token>` | Hedef sunucu için kimlik doğrulama belirteci |
|
||
| `--tag <tag>` | npm dist-tag (örn. `beta`, `next`) — yalnızca npm yayımlama için |
|
||
|
||
## Uygulama kaydı
|
||
|
||
Bir uygulama bir çalışma alanına kurulmadan önce kaydedilmelidir. Kayıt, uygulamanın nereden geldiğini ve nasıl kimlik doğrulanacağını açıklayan bir meta veri kaydıdır. Bu, çoğu durumda CLI tarafından otomatik olarak gerçekleştirilir.
|
||
|
||
### Kaynak türleri
|
||
|
||
Her kaydın, kurulum sırasında uygulamanın dosyalarının nasıl çözümleneceğini belirleyen bir kaynak türü vardır:
|
||
|
||
| Kaynak türü | Dosyaların nasıl çözümlendiği | Tipik kullanım durumu |
|
||
| ----------- | ----------------------------------------------------------------------------------- | ------------------------------------------ |
|
||
| `LOCAL` | Dosyalar, CLI izleyici tarafından gerçek zamanlı olarak eşitlenir — kurulum atlanır | `app:dev` ile geliştirme |
|
||
| `NPM` | `sourcePackage` alanı aracılığıyla npm kayıt defterinden alınır | npm'de yayımlanan uygulamalar |
|
||
| `TARBALL` | Sunucuda depolanan, yüklenmiş bir `.tgz` dosyasından çıkarılır | `--server` ile yayımlanan özel uygulamalar |
|
||
|
||
### Kayıt nasıl gerçekleşir
|
||
|
||
* **`app:dev`** — bir çalışma alanına karşı geliştirme modunu ilk kez çalıştırdığınızda otomatik olarak bir `LOCAL` kaydı oluşturur.
|
||
* **`app:publish --server`** — bir tarball yükler ve bir `TARBALL` kaydı oluşturur (veya günceller), ardından uygulamayı kurar.
|
||
* **npm pazar yeri** — uygulamalar npm kayıt defterinden Twenty pazar yeri kataloğuna eşitlendiğinde `NPM` kayıtları oluşturulur.
|
||
* **GraphQL API** — `createApplicationRegistration` mutasyonu aracılığıyla programatik olarak da kayıtlar oluşturabilirsiniz.
|
||
|
||
### Kayıt ve kurulum
|
||
|
||
**Kayıt** ve **kurulum** ayrı kavramlardır:
|
||
|
||
* Bir kayıt (`ApplicationRegistration`), uygulamayı tanımlayan genel bir meta veri kaydıdır: adı, kaynak türü, OAuth kimlik bilgileri ve pazar yeri listeleme durumu. Herhangi bir çalışma alanından bağımsız olarak var olur.
|
||
* Bir kurulum (`Application`), çalışma alanı başına bir örnektir. Bir kullanıcı bir uygulamayı kurduğunda, Twenty paketi kaydın kaynağından çözümler, derlenen dosyaları depolamaya yazar ve manifesti (nesneler, alanlar, mantık işlevleri vb. oluşturarak) eşitler o çalışma alanında.
|
||
|
||
Bir kayıt birçok çalışma alanına kurulabilir. Her çalışma alanı, uygulamanın dosyalarının ve veri modelinin kendi kopyasını alır.
|
||
|
||
### OAuth kimlik bilgileri
|
||
|
||
Her kayıt, oluşturma sırasında üretilen OAuth kimlik bilgilerini (`oAuthClientId` ve `oAuthClientSecret`) içerir. Bunlar, kullanıcılar adına API isteklerini kimlik doğrulamak için uygulama tarafından kullanılır. İstemci gizli anahtarı oluşturma sırasında yalnızca bir kez sağlanır — onu güvenli bir şekilde saklayın. Bunu daha sonra `rotateApplicationRegistrationClientSecret` mutasyonu aracılığıyla yenileyebilirsiniz.
|
||
|
||
## Manuel kurulum (scaffolder olmadan)
|
||
|
||
En iyi başlangıç deneyimi için `create-twenty-app` kullanmanızı önersek de, bir projeyi manuel olarak da kurabilirsiniz. CLI'yi global olarak kurmayın. Bunun yerine `twenty-sdk`'yi yerel bir bağımlılık olarak ekleyin ve package.json içinde tek bir betik tanımlayın:
|
||
|
||
```bash filename="Terminal"
|
||
yarn add -D twenty-sdk
|
||
```
|
||
|
||
Ardından bir `twenty` betiği ekleyin:
|
||
|
||
```json filename="package.json"
|
||
{
|
||
"scripts": {
|
||
"twenty": "twenty"
|
||
}
|
||
}
|
||
```
|
||
|
||
Artık tüm komutları `yarn twenty <command>` üzerinden çalıştırabilirsiniz; örn. `yarn twenty dev`, `yarn twenty help` vb.
|
||
|
||
## Sorun Giderme
|
||
|
||
* Kimlik doğrulama hataları: `yarn twenty auth:login` çalıştırın ve API anahtarınızın gerekli izinlere sahip olduğundan emin olun.
|
||
* Sunucuya bağlanılamıyor: API URL’sini ve Twenty sunucusunun erişilebilir olduğunu doğrulayın.
|
||
* Türler veya istemci eksik/eski: `yarn twenty dev` komutunu yeniden çalıştırın — tiplendirilmiş istemciyi otomatik olarak oluşturur.
|
||
* Geliştirme modu eşitlenmiyor: `yarn twenty dev`'in çalıştığından ve değişikliklerin ortamınız tarafından yok sayılmadığından emin olun.
|
||
|
||
Discord Yardım Kanalı: https://discord.com/channels/1130383047699738754/1130386664812982322
|