استخدام Runtime SDK
الهدف
فهم Runtime SDK، الواجهة الوحيدة التي يصل بها تطبيق React مخصص أو عنصر تحكم مخصص إلى منصة Parsware، واستخدام كل جزء منه استخدامًا صحيحًا.
المتطلبات المسبقة
- مشروع مُهيَّأ كما في بناء تطبيق React مخصص أو
بناء عنصر تحكم مخصص، مع تثبيت
@parsware/runtime-sdkمنhttps://docs.parsware.com/sdk/parsware-runtime-sdk-1.0.0.tgz. - إعلان
@parsware/runtime-sdkبوصفه singleton مشتركًا في ضبط Module Federation للمشروع. ومن دون ذلك يحصل التطبيق على نسخته الفارغة من SDK بدلًا من نسخة الواجهة.
القاعدة الوحيدة
لا يصل العنصر المستضاف إلى المنصة إلا عبر Runtime SDK. تربط الواجهة runtime بالبيئة التي فتحت فيها التطبيق وبالشخص الذي يستخدمه. لا معرّف بيئة يُمرَّر، ولا رمز يُخزَّن، ولا عنوان يُضبط، فيعمل البناء نفسه في كل بيئة يُثبَّت فيها. والتطبيق الذي يستدعي واجهات HTTP البرمجية للمنصة بنفسه عليه أن يحل الأمور الثلاثة، ويخطئ فيها أول مرة يُثبَّت في مكان آخر.
الحصول على runtime
يقرؤه تطبيق React المخصص من hook:
import { useParswareRuntime } from '@parsware/runtime-sdk';
export default function App() {
const runtime = useParswareRuntime();
// …
}
ويتلقى عنصر التحكم المخصص السطح نفسه في الخاصية context:
export default function Control({ value, onChange, fieldMetadata, context }: ParswareControlProps) {
context.log('debug', 'mounted');
}
خارج الواجهة، في اختبار وحدة، غلّف المكوّن بـ ParswareRuntimeProvider مع runtime بديل:
<ParswareRuntimeProvider runtime={stub}>.
ما يحمله runtime
| العضو | ما هو |
|---|---|
environmentId، environmentName | البيئة التي فُتح فيها التطبيق. يمكن عرض الاسم. |
artifactId | الاسم المنطقي للتطبيق أو عنصر التحكم. |
user | الشخص الذي يستخدمه: id، name، email، tenantId، roles. |
scopes | ما طلبه التطبيق، مقصورًا على ما تحمله جلسة ذلك الشخص. |
data | الجداول، والسجلات، والاستعلامات، والاستعلامات المحفوظة، والواجهات البرمجية المخصصة، والتقارير، وملفات السجلات، مربوطة بالبيئة. |
storage | تخزين الكائنات: الحاويات (buckets) والملفات التي فيها. |
log | تسجيل منظَّم يُرسَل إلى المنصة مع التطبيق والبيئة. |
من يستخدمه، وما المسموح له
runtime.user للعرض والترحيب: اسم، ودور يُختار على أساسه تخطيط. وليس تحققًا من الصلاحيات.
كل استدعاء يتم باسم الشخص الذي يستخدم التطبيق. دوره الأمني يقرر ما يحق لكل استدعاء قراءته وتغييره، تمامًا كما في التطبيق الذي يعتمد على النموذج؛ فلا يستطيع التطبيق أبدًا أن يفعل أكثر مما يستطيعه من فتحه، وشخصان يستخدمان التطبيق نفسه يرى كل منهما ما يُسمح له به.
runtime.scopes هي requestedScopes الخاصة بالتطبيق مقصورة على جلسة الشخص. اقرأها لتستبعد ما لن
يعمل:
const canSave = runtime.scopes.includes('storage.write');
قراءة السجلات
صفحة من جدول واحد
import { FilterConnector, RecordFilterOperator } from '@parsware/runtime-sdk';
const page = await runtime.data.queryRecords('par_order', {
pageNumber: 1,
pageSize: 25,
sorts: [{ field: 'par_createdon', descending: true }],
filterGroup: {
connector: FilterConnector.And,
conditions: [{ field: 'par_status', operator: RecordFilterOperator.Equals, value: 1 }],
},
});
// page.items, page.totalCount, page.hasNextPage …
صفحة، لا الجدول كله أبدًا. و{ pageSize: 1 } هي الطريقة الرخيصة لقراءة عدد فقط.
استعلام: أعمدة وروابط ومجاميع
يأخذ runQuery مستند الاستعلام نفسه الذي ترسله جداول المنصة نفسها، فيستطيع اختيار الأعمدة وتتبّع
حقول البحث والجمع:
const result = await runtime.data.runQuery('nw_visitor', {
select: ['nw_name', 'nw_company', 'new_purpose'],
order: [{ field: 'nw_name', descending: false }],
page: { number: 1, size: 50 },
});
for (const record of result.records?.items ?? []) {
record.values.nw_name; // 'Ada Lovelace'
record.values.new_purpose; // 270150000 — what is stored
record.formattedValues?.new_purpose; // 'Meeting' — what a person reads
}
ومع كتلة aggregate يجيب بمجموعات بدلًا من صفوف (result.kind === 'Groups'). اطلب المجاميع من
الخادم بدلًا من جلب كل صف لجمعه في المتصفح.
استعلام محفوظ، باسمه
حين يكون السؤال من شأن الحل لا من شأن شيفرة التطبيق، احفظه في الحل وشغّله باسمه:
const totals = await runtime.data.runSavedQuery('nw_visitorsbycompany');
for (const group of totals.groups?.groups ?? []) {
group.groups.nw_company; // 'Analytical Engines'
group.measures.visitors; // 4
}
عندئذ ينتقل التعريف مع الحل ويمكن تحسينه دون نشر بناء جديد. ولا تستطيع الخيارات إلا تضييقه:
يُضَم filter الإضافي إلى المحفوظ بـ AND، ويحدّه page أو top. أما ما يقرؤه ويجمّعه ويعدّه
فيبقى ما في الاستعلام المحفوظ.
حقول البحث والخيارات
searchLookup(table, { search })يجد المرشحين لحقل بحث، مع الأعمدة التي يعرضها عرض البحث في الجدول الهدف. و**resolveLookup(table, id)** يعيد المعرّف المحفوظ إلى النص الذي يُعرض.- يصل عمود الخيارات رقمَ الخيار، وتسميته بجانبه. تحمل
valuesالرقم المحفوظ وتحملformattedValuesالتسمية التي يقرؤها الشخص، كما يصل اسم حقل البحث. وتحملها النتائج المجمّعة فيformattedGroups. وlistOptionSets()يعطي القائمة كاملة، لأداة اختيار.
تغيير السجلات
const created = await runtime.data.createRecord('par_order', { par_name: 'ORD-1001' });
await runtime.data.updateRecord('par_order', created.id, { par_status: 2 });
await runtime.data.deleteRecord('par_order', created.id);
يأخذ updateRecord الأعمدة التي تتغير فقط. إعادة إرسال السجل كاملًا تكت ب فوق قيم ربما غيّرها
شخص آخر منذ أن قرأتها. تعمل خطوات المكوّن الإضافي على الخادم عند هذه الكتابات كما عند غيرها؛ أما
قواعد العمل من جهة العميل في النموذج فلا، إذ لا نموذج هنا.
استدعاء واجهة برمجية مخصصة
const overview = await runtime.data.callApi('GET', 'psl/v1/overview', { season: '2026' });
المسار هو عنوان الواجهة البرمجية تحت البيئة: بادئة الناشر، ثم الإصدار، ثم المسار. في GET
وDELETE تذهب المدخلات في سلسلة الاستعلام، وفي غيرهما في جسم JSON. والرفض يرمي خطأ برسالة
الواجهة البرمجية نفسها. والتطبيق والنظام الخارجي اللذان يستدعيان الواجهة البرمجية نفسها يقرآن
الإجابة نفسها.
تخزين الكائنات
للملفات التي لا تخص سجلًا بعينه: تصدير صنعه التطبيق، أو مستند سلّمه إليه أحدهم، أو دفعة يوشك على معالجتها.
await runtime.storage.createBucket('nw-frontdesk'); // or returns the existing one
const saved = await runtime.storage.put('nw-frontdesk', 'visitors/2026-10-14.csv',
new Blob([csv], { type: 'text/csv' }), '2026-10-14.csv');
const files = await runtime.storage.list('nw-frontdesk', 'visitors/');
const blob = await runtime.storage.get('nw-frontdesk', saved.key);
await runtime.storage.remove('nw-frontdesk', saved.key);
- الحاوية ملك للمستأجر لا للبيئة. تتشاركها بيئتان للمستأجر نفسه، فضع البيئة في المفتاح إن وجب أن تبقى ملفاتهما منفصلة.
- أسماء الحاويات مشتركة بين كل المستأجرين، كما في S3. اختر اسمًا خاصًا بك.
- اطلب
storage.readوstorage.writeفيrequestedScopesالخاصة بالتطبيق. - الملف الذي يخص سجلًا واحدًا يوضع على السجل نفسه بدلًا من ذلك (القسم التالي).