الانتقال إلى المحتوى الرئيسي
دوال المنطق هي دوال TypeScript على جانب الخادم تعمل على منصة Twenty. يمكن تشغيلها بواسطة طلبات HTTP أو جداول cron أو أحداث قاعدة البيانات — كما يمكن إتاحتها كأدوات لوكلاء الذكاء الاصطناعي.
كل ملف وظيفة يستخدم defineLogicFunction() لتصدير تكوين مع معالج ومشغّلات اختيارية.
src/logic-functions/createPostCard.logic-function.ts
أنواع المشغّلات المتاحة:
  • httpRoute: يعرِض وظيفتك على مسار وطريقة HTTP. في شيفرة التطبيق، أضف البادئة /s/ إلى مسار التوجيه عند استخدام RestApiClient؛ يستخدم عنوان URL المنشور قاعدة TWENTY_FUNCTIONS_URL المُحدَّدة (أو \<server-url>/s إذا لم تُحدَّد).
لاستدعاء دالة منطقية يتم تشغيلها بواسطة مسار من مكون واجهة (بدون واجهة رسومية)، راجع قسم استدعاء دالة منطقية.
  • cron: يشغّل وظيفتك على جدول باستخدام تعبير CRON.
  • databaseEvent: يعمل على أحداث دورة حياة كائنات مساحة العمل. عندما تكون عملية الحدث هي updated، يمكن تحديد الحقول المحددة المراد الاستماع إليها في مصفوفة updatedFields. إذا تُركت غير معرّفة أو فارغة، فسيؤدي أي تحديث إلى تشغيل الدالة.
مثال: person.updated، *.created، company.*
  • serverRoute: يوفّر مسار HTTP واحدًا بنطاق التسجيل. تعمل دالة resolver (المُعلَنة باستخدام serverRouteTriggerSettings) في مساحة عمل المالك وتُرجِع مساحة العمل المستهدفة ودالة المنطق المستهدفة التي يجب التوجيه إليها؛ ثم يُشغِّل النظام الأساسي تلك الدالة المستهدفة ويُرجِع استجابتها. راجع مشغّل مسار الخادم.
يمكنك أيضًا تنفيذ دالة يدويًا باستخدام CLI:
يمكنك متابعة السجلات باستخدام:

حمولة مشغل المسار

عندما يستدعي مُشغِّل المسار وظيفتك المنطقية، فإنها تتلقّى كائن RoutePayload الذي يتبع صيغة AWS HTTP API v2. استورد نوع RoutePayload من twenty-sdk/logic-function:
يحتوي نوع RoutePayload على البنية التالية:

forwardedRequestHeaders

افتراضيًا، لا تُمرَّر رؤوس HTTP من الطلبات الواردة إلى دالتك المنطقية لأسباب أمنية. للوصول إلى رؤوس محددة، أدرِجها في مصفوفة forwardedRequestHeaders:
في معالجك، يمكنك الوصول إلى الرؤوس المُمرَّرة بهذه الطريقة:
تُحوَّل أسماء الرؤوس إلى أحرف صغيرة. يمكنك الوصول إليها باستخدام مفاتيح بأحرف صغيرة (على سبيل المثال، event.headers['content-type']).

استجابة HTTP مخصصة

بشكل افتراضي، فإن إرجاع قيمة بسيطة من المعالج الخاص بك يعيدها كاستجابة 200 (بصيغة JSON للكائنات وtext/plain للسلاسل النصية). للتحكم في رمز الحالة ورؤوس الاستجابة، أعد كائن Response من twenty-sdk/logic-function:
لأسباب أمنية، يتم تقييد ترويسات الاستجابة بقائمة مسموح بها. يتم إسقاط أي ترويسة ليست في القائمة (مثل Set-Cookie، وترويسات CORS مثل Access-Control-Allow-Origin، أو ترويسات X-* المخصصة) بصمت قبل إرسال الاستجابة. ترويسات الاستجابة المسموح بها هي:
  • content-type
  • content-language
  • content-disposition
  • cache-control
  • retry-after
يجب أن يكون رمز الحالة رمز حالة HTTP صالحًا (بين 100 و599). تتم مطابقة أسماء ترويسات الاستجابة دون حساسية لحالة الأحرف.

مشغّل مسار الخادم

httpRouteTriggerSettings يوفّر دالة تحت ‎/s/‎ ويحل مساحة العمل من مضيف الطلب — وهذا يعمل عندما تكون لكل مساحة عمل نطاقها الخاص. لكن المزوّدين من جهات خارجية يرسلون أحداث كل مستأجر إلى عنوان URL واحد. في هذه الحالة، استخدم serverRouteTriggerSettings.يتكوّن المشغّل من جزأين:
  1. دالة منطق resolver — يتم التصريح عنها باستخدام serverRouteTriggerSettings — تعمل في مساحة العمل المالكة (مساحة العمل التي تمتلك تسجيل التطبيق). تتفحّص الطلب الوارد وتُرجِع { workspaceId, targetLogicFunctionUniversalIdentifier, payload? }، لتحديد كلٍ من مساحة العمل المستهدفة والدالة المستهدفة. يُعَدّ الـ resolver نقطة التفويض الوحيدة — فعنوان URL يحمل فقط معرّف الـ resolver. هذا هو المكان المفضّل للتحقق من تواقيع الطلبات: يعمل الـ resolver قبل أي تأثير جانبي، ولديه إمكانية الوصول إلى rawBody الأصلي والرؤوس المُمرَّرة، ويمكنه رفض الطلب دون لمس الهدف مطلقًا.
  2. دالة منطق target — دالة منطق عادية لكل مساحة عمل — تعمل بعد ذلك في مساحة العمل التي تم حلّها باستخدام الحمولة التي أعادها الـ resolver (أو حمولة الطلب الأصلية إذا لم يقم الـ resolver بتحويلها). تصبح القيمة التي تعيدها هي استجابة HTTP.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
يمكن الوصول إلى نقطة النهاية عند:
المعرّف هو universalIdentifier الخاص بالـ resolver من ملف manifest لديك. سجّل عنوان URL هذا لدى المزوّد.
يجب أن يتم المطالبة بالتطبيق وتثبيته في مساحة عمل المالك الخاصة به. نظرًا لأن محلِّل الاستدعاء يعمل في مساحة عمل المالك (مساحة العمل التي تمتلك تسجيل التطبيق)، فإن مشغّل مسار الخادم يعمل فقط بمجرد أن يكون قد تم المطالبة بالتطبيق — أي أصبح لديه مساحة عمل مالكة — و تم تثبيت هذا التطبيق في مساحة عمل المالك. إلى أن يتحقق الشرطان معًا، فلن يكون لدى محلِّل الاستدعاء مكان يعمل فيه، وبالتالي لا يمكن إرسال المسار. لذلك لا يمكن إدراج أي تطبيق يعرِّض دالة منطقية serverRouteTriggerSettings في السوق حتى تتم المطالبة به وتثبيته في مساحة عمل المالك الخاصة به.
عقد الـ Resolver. يفرض نوع LogicFunctionConfig في حزمة SDK هذا في وقت الترجمة: بمجرد تعيينك لـ serverRouteTriggerSettings، يُقيَّد الـ handler الخاص بك بأن يُرجِع { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } (أو Promise من هذا الكائن). يجب أن يكون workspaceId لمساحة عمل تكون الدالة المستهدفة مثبّتة فيها، وإلا فسيتم رفض الطلب مع 404.
مسؤولية التحقق من التوقيع تقع عليك — تحقّق في الـ resolver. المنصّة لا تتحقق من تواقيع الطلبات. يُعَدّ الـ resolver المكان الموصى به للقيام بذلك: فهو يعمل أولًا، مع إمكانية الوصول إلى event.rawBody والرؤوس التي أدرجتها في forwardedRequestHeaders، وأي خطأ يتم رميه (أو أي workspaceId لا يطابق) يوقف عملية الإرسال قبل استدعاء الهدف. إذا دفعت التحقق بدلًا من ذلك إلى داخل الهدف، فيجب على الهدف أن يكون حذرًا حتى لا يفقد rawBody والرؤوس — أي يجب ألّا يعيد الـ resolver خاصية payload. تحقّق دائمًا قبل أي تأثير جانبي، واستخدم مقارنة بزمن ثابت.
بالنسبة لتواقيع الطلبات، يستخدم معظم المزوّدين HMAC-SHA256 للتوقيع؛ الأجزاء التي تختلف هي اسم الرأس وترميز الملخّص وسلسلة الحمولة الموقّعة. بعض الأمثلة:يُظهِر مثال الـ resolver أعلاه بالفعل تدفّق GitHub HMAC-SHA256 — عدِّل اسم الرأس وترميز الملخّص وسلسلة الحمولة الموقّعة بحسب المزوّد الذي تدمجه.
يعمل الهدف بشكل متزامن وتصبح القيمة التي يعيدها هي استجابة HTTP، لذا يرى المتّصلون رمز الحالة الخاص بك ويمكنهم إعادة المحاولة عند رموز غير 2xx. اجعل كلا المعالِجَيْن سريعين — بعض المزوّدين (مثل Slack) تنتهي مهلة طلباتهم خلال بضع ثوانٍ. نظرًا لأن الـ resolver يمكن الوصول إليه كنقطة نهاية عامة، قم بحمايته من خلال تحديد المعدّل (rate limiting) على الحافة لديك.

حمولة مُحفِّز حدث قاعدة البيانات

عندما يستدعي مُحفِّز حدث قاعدة البيانات دالة المنطق الخاصة بك، فإنه يستقبل كائن DatabaseEventPayload واحدًا لكل سجل تم تغييره. تجمع الحمولة بين البيانات الوصفية حول مساحة العمل والكائن المصدر وبين الحدث على مستوى السجل.
تتضمن الحمولة ما يلي:في عمليات الحذف اللين (soft deletes)، يتبع .deleted بنية نمط التحديث لأن حقل deletedAt في السجل يتغيّر. في عمليات الحذف الدائم، استخدم .destroyed.
databaseEventTriggerSettings.updatedFields يرشّح أيّ أحداث التحديث التي تُشغِّل الدالة. event.properties.updatedFields يوضّح لك أي الحقول تغيّرت فعليًا في الحدث الحالي.
مثال على حدث الإنشاء:
مثال على حدث التحديث:
تشغيل المشغّل فقط عند تحديثات البريد الإلكتروني:
مثال على حدث الحذف:

إتاحة دالة كأداة ذكاء اصطناعي أو كإجراء ضمن سير العمل

يمكن إتاحة دوال المنطق على واجهتين، ولكلٍ منهما مشغِّل خاص به:
  • toolTriggerSettings — يجعل الدالة قابلة للاكتشاف عبر ميزات الذكاء الاصطناعي الخاصة بـ Twenty (الدردشة، MCP، استدعاء الدوال). يستخدم JSON Schema القياسي، وهو التنسيق الذي تفهمه LLMs أصلاً.
  • workflowActionTriggerSettings — يجعل الدالة تظهر كخطوة في منشئ سير العمل المرئي. يستخدم InputSchema الغني الخاص بـ Twenty لكي يتمكن المُنشئ من عرض محرّرات الحقول المناسبة، وأدوات انتقاء المتغيّرات، والتسميات.
يمكن للدالة اختيار أحدهما، أو الآخر، أو كليهما. توجد جنبًا إلى جنب مع cronTriggerSettings وdatabaseEventTriggerSettings وhttpRouteTriggerSettings — النمط نفسه، والشكل نفسه.
العلاقة بإجراء Code الخاص بسير العمل. يُعَد إجراء Code المضمَّن في منشئ سير العمل دالة منطقية بحد ذاته — حيث ينشئ Twenty واحدًا لكل خطوة Code ويعرض محرره مضمّنًا. تُستخدَم workflowActionTriggerSettings لتحويل هذا الكود المضمَّن لمرة واحدة إلى إجراء قابل لإعادة الاستخدام: عرِّف الدالة مرة واحدة في تطبيقك وستصبح قابلة للاختيار في أي سير عمل، بدلاً من نسخها ولصقها في كل خطوة Code. راجع إجراء Code في دليل المستخدم لعرض منظور المستخدم النهائي.
src/logic-functions/enrich-company.logic-function.ts
النقاط الرئيسية:
  • يمكن للدالة مزج الواجهات — صرِّح بكلٍ من toolTriggerSettings وworkflowActionTriggerSettings لإتاحتها في الدردشة وفي منشئ سير العمل.
  • toolTriggerSettings.inputSchema وworkflowActionTriggerSettings.inputSchema كلاهما اختياري. عند الإغفال، يستنتج مُنشئ البيان هذه المخططات من الشيفرة المصدرية للمعالج (JSON Schema لأداة الذكاء الاصطناعي، وInputSchema الخاصة بـ Twenty لإجراء سير العمل). قدّم واحدًا صراحةً عندما ترغب في أنواع أكثر ثراءً — على سبيل المثال، مع حقول واعية بـ FieldMetadataType مثل CURRENCY أو RELATION لمنشئ سير العمل، أو مع حقول description التي يمكن لوكيل الذكاء الاصطناعي قراءتها:
للتصريح بمعاملاتك مرة واحدة وخدمة كلتا الواجهتين، عرّف مخطط JSON واحد (InputJsonSchema) وحوِّله لاستخدامه في إجراء سير العمل باستخدام jsonSchemaToInputSchema من twenty-sdk/logic-function. toolTriggerSettings.inputSchema يستخدم مخطط JSON مباشرة، بينما workflowActionTriggerSettings.inputSchema يتوقّع InputSchema الخاص بـ Twenty:
مثال كامل لإجراء سير عمل
تقبل workflowActionTriggerSettings أربعة حقول:تجميع ذلك معًا — دالّة معروضة كإجراء سير عمل، مع مخرَج مُعلَن بحيث يمكن للخطوات اللاحقة الرجوع إلى taskId:
src/logic-functions/enrich-company.logic-function.ts
بمجرد تثبيت التطبيق، سيظهر Enrich Company في منتقّي الإجراءات في منشئ سير العمل. يعرض المُنشئ companyName وdomain كحقول إدخال (كلٌّ منهما قادر على سحب القيم من الخطوات السابقة)، ويمكن للخطوات اللاحقة الرجوع إلى مخرجات الخطوة taskId وenriched.
اكتب description جيدًا. يعتمد وكلاء الذكاء الاصطناعي على حقل description الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها.
مساعدات وقت التشغيل. يقوم twenty-sdk/utils بإعادة تصدير مساعدات صغيرة لوقت التشغيل حتى لا تستورد المعالجات مباشرةً من twenty-shared. على سبيل المثال، تُرجِع isDefined(value) القيمة false لكلٍّ من null وundefined — استخدمها لتضييق نطاق مُدخلات المعالِجات الاختيارية بأمان، والتي يمكن أن تصل كقيمة null أثناء وقت التشغيل حتى عندما تكون مكتوبة كـ T | undefined:
خطافات التثبيت — معالجات ما قبل التثبيت وما بعد التثبيت — تشترك في وقت التشغيل نفسه، ولكن يُصرَّح عنها بدوال تعريف خاصة بها ولا تأخذ إعدادات المشغّلات. راجع خطافات التثبيت (Install Hooks) لمعرفة definePreInstallLogicFunction و definePostInstallLogicFunction.

عملاء واجهة برمجة تطبيقات مضبوطة الأنواع (twenty-client-sdk)

توفر حزمة twenty-client-sdk عميلين لـ GraphQL ذوي أنواع ثابتة للتفاعل مع واجهة Twenty البرمجية من وظائفك المنطقية ومكوّنات الواجهة الأمامية.
CoreApiClient هو العميل الرئيسي للاستعلام وتعديل بيانات مساحة العمل. يُولَّد من مخطط مساحة العمل لديك أثناء yarn twenty dev أو yarn twenty dev:build، لذا فهو مضبوط الأنواع بالكامل ليتوافق مع كائناتك وحقولك.
يستخدم العميل صياغة مجموعة اختيار: مرِّر true لتضمين حقل، واستخدم __args للوسيطات، وعشّش الكائنات للعلاقات. ستحصل على إكمال تلقائي كامل وفحص للأنواع يعتمد على مخطط مساحة العمل لديك.
يتم توليد CoreApiClient في وقت التطوير/البناء. إذا استخدمته دون تشغيل yarn twenty dev أو yarn twenty dev:build أولًا، فسيؤدي ذلك إلى خطأ. تحدث عملية التوليد تلقائيًا — إذ يستطلع CLI مخطط GraphQL لمساحة عملك وينشئ عميلًا مضبوط الأنواع باستخدام @genql/cli.

استخدام CoreSchema للتعليقات التوضيحية للأنواع

CoreSchema يوفّر أنواع TypeScript المطابقة لكائنات مساحة العمل لديك — مفيد لتعيين أنواع حالة المكوّن أو معاملات الدوال:
يأتي MetadataApiClient مُجهّزًا مسبقًا مع SDK (لا حاجة للتوليد). يستعلم عن نقطة النهاية /metadata للحصول على تكوين مساحة العمل والتطبيقات ورفع الملفات.

رفع الملفات

يتضمن MetadataApiClient طريقة uploadFile لإرفاق الملفات بالحقول من نوع الملف:
النقاط الرئيسية:
  • يستخدم universalIdentifier الخاص بالحقل (وليس معرّفه الخاص بمساحة العمل)، بحيث يعمل كود الرفع لديك عبر أي مساحة عمل مُثبَّت فيها تطبيقك.
  • العنوان url المُعاد هو عنوان URL موقّع يمكنك استخدامه للوصول إلى الملف المرفوع.
عند تشغيل كودك على Twenty (وظائف منطقية أو مكوّنات أمامية)، يقوم النظام الأساسي بحقن بيانات الاعتماد كمتغيرات بيئية:
  • TWENTY_API_URL — عنوان URL الأساسي لواجهة Twenty البرمجية
  • TWENTY_APP_ACCESS_TOKEN — مفتاح قصير العمر ذو نطاق يقتصر على الدور الافتراضي لوظيفة تطبيقك
لست بحاجة إلى تمرير هذه القيم إلى العملاء — فهي تُقرأ تلقائيًا من process.env. تُحدَّد أذونات مفتاح واجهة برمجة التطبيقات بواسطة الدور المُعلن باستخدام defineApplicationRole() (أو المشار إليه عبر defaultRoleUniversalIdentifier في application-config.ts).