
Andrew Hanna

Andrew Hanna

سنتناول عدة موضوعات تجعل عملك أسهل، سواء كنت تستخدم بالفعل second generation managed packages أو كنت جديدا على هذا الموضوع.
عندما تنشئ إصدارا جديدا، قد يستغرق ذلك الكثير من الوقت والجهد، وعند فشله لا يقدم أحيانا رسائل خطأ مفيدة بما يكفي لمساعدتك على حل المشكلة. كنت أتمنى حقا أن يقدم رسائل خطأ أكثر تفصيلا مثل أمر sfdx deploy. ومع ذلك، فهذا ليس الجزء الأصعب بعد. يصبح الأمر أكثر تعقيدا عندما تكون الحزمة قد أُنشئت وأصبحت جاهزة للاستخدام، لكن عند محاولة تثبيتها في test org تكتشف أن الكثير من الميزات لا تعمل كما هو متوقع.
إذا كنت تريد تجنب هذا الارتباك أو تواجهه بالفعل، فأنت في المكان الصحيح لتتعلم من تحدياتنا وأخطائنا وتوفر على نفسك صداعا غير ضروري.
لنبدأ بالتعرف إلى معدلات الوصول المختلفة في كلاسات Apex حتى تساعدك على اختيار طريقة تنفيذ الكود:
هذا هو معدل الوصول الافتراضي، ويعني أن method أو variable متاح فقط داخل كلاس Apex الذي تم تعريفه فيه. إذا لم تحدد معدل وصول، فستكون method أو variable خاصة.
يعني ذلك أن method أو variable مرئي لأي inner classes داخل كلاس Apex المعرِّف، وكذلك للكلاسات التي ترث من ذلك الكلاس. يمكنك استخدام معدل الوصول هذا فقط مع instance methods وmember variables. هذا الإعداد أكثر سماحية من الإعداد الافتراضي private، تماما كما في Java.
يعني ذلك أن method أو variable متاح لكل Apex داخل package محددة.
في Apex، لا يعمل معدل الوصول public بالطريقة نفسها التي يعمل بها في Java. تم ذلك لتقليل ربط التطبيقات ببعضها وإبقاء كود كل تطبيق منفصلا. في Apex، إذا أردت جعل شيء ما عاما كما في Java، فيجب استخدام معدل الوصول global.
لإتاحة الوصول لكل second-generation (2GP) managed packages التي تشترك في namespace، استخدم public مع annotation @NamespaceAccessible. استخدام معدل الوصول public في packages من دون namespace يجعل كود Apex ضمنيا @NamespaceAccessible.
يعني ذلك أن method أو variable يمكن أن يستخدمه أي كود Apex لديه وصول إلى الكلاس، وليس فقط كود Apex داخل التطبيق نفسه. يجب استخدام معدل الوصول هذا لأي method يجب الرجوع إليها من خارج التطبيق، سواء عبر SOAP API أو بواسطة كود Apex آخر. إذا أعلنت method أو variable كـ global، فيجب أيضا إعلان الكلاس الذي يحتويها كـ global.
نوصي باستخدام معدل الوصول global نادرا، إن كان لا بد من استخدامه. فالاعتماديات بين التطبيقات صعبة الصيانة.
تمكّن annotation @AuraEnabled الوصول من جهة العميل وجهة الخادم إلى Apex controller method. إضافة هذه annotation تجعل methods لديك متاحة لمكونات Lightning، سواء Lightning web components أو Aura components. لا يتم كشف إلا methods التي تحمل هذه annotation.
في API version 44.0 وما بعده، يمكنك تحسين أداء runtime عبر تخزين نتائج method على العميل باستخدام annotation @AuraEnabled(cacheable=true). يمكنك تخزين نتائج methods فقط للـ methods التي تسترجع البيانات ولا تعدلها. استخدام هذه annotation يلغي الحاجة إلى استدعاء setStorable() في JavaScript code عند كل action يستدعي Apex method.
في API version 55.0 وما بعده، يمكنك استخدام annotation @AuraEnabled(cacheable=true scope='global') لتمكين تخزين Apex methods في global cache.
global with sharing class TestClass{
@AuraEnabled
global static void testMethod{}
}
يمكنك الرجوع إلى هذه method من package مختلفة داخل Lightning Web
Component بهذه الطريقةimport logExceptionLWC from "@salesforce/apex/TestClass.testMethod";
من أهم الحيل أنه عندما تنشئ model مخصصا في كود Apex وقد تحتاج لاحقا إلى serialize أو deserialize له، فستحتاج بالتأكيد إلى إضافة Json Access annotation للـ custom model. تلميح: تأكد من إضافة annotation إلى child objects أيضا ;)
إذن ما Json Access annotation؟ وما خيارات واعتبارات serialization و deserialization الخاصة بها؟
تتحكم annotation @JsonAccess المعرفة على مستوى Apex class فيما إذا كان يمكن serialize أو deserialize لنسخ الكلاس. إذا قيدت annotation عمليات JSON serialization وdeserialization، فسيتم رمي runtime JSONException.
تفرض المعلمتان serializable وdeserializable في annotation @JsonAccess السياقات التي يسمح فيها Apex بعمليات serialization وdeserialization.
يمكنك تحديد معلمة واحدة أو كلتيهما، لكن لا يمكنك تحديد annotation دون معلمات. القيم الصالحة للمعلمات التي تحدد ما إذا كانت serialization و deserialization مسموحتين:
إذا تم توسيع Apex class يحمل JsonAccess، فلن يرث الكلاس الموسع هذه الخاصية. إذا طُبقت method toString على objects يجب ألا يتم serialize لها، فقد يتم كشف بيانات خاصة. يجب عليك override للـ toString method على objects التي يجب حماية بياناتها. مثلا، عند serialize لكائن مخزن كمفتاح في Map، يتم استدعاء toString method. تتضمن الخريطة الناتجة entries من key (string) وvalue، وبذلك تكشف كل حقول الكائن.
@JsonAccess(serializable='always' deserializable='always')
global class FullDetails{
global String fullName {get; set;}
global Address address {get; set;}
@JsonAccess(serializable='always' deserializable='always')
global class Address{
global String streetName {get; set;}
global String postalCode {get; set;}
}
}
ماذا لو كنت تنشئ بعض apex classes غير المعبأة داخل مشروعك، وقد تحتاج إلى الرجوع إلى apex class آخر أو interface يمثل جزءا من managed package؟ تأكد فقط من تعيين هذه الكلاسات كـ global والرجوع إلى الكلاس عبر <Namespace>.<ClassName>
عادة ما يكون مرجع callout هو callout:<NamedCredentialsApiName>، لكن إذا كنت تستخدم named credentials كجزء من package لكلاسات callout، فستحتاج إلى تحديثه إلى callout:<NampeSpace>__<NamedCredentialsApiName>
إذا كنت تضيف مكون connected app إلى package لديك، فهذه هي الخطوات الدقيقة للقيام بذلك وتجنب بعض الأخطاء الشائعة.
<?xml version="1.0" encoding="UTF-8"?> <ConnectedApp xmlns="http://soap.sforce.com/2006/04/metadata"> <developerName>YourNamespace__ConnectedAppName</developerName> <contactEmail>ConnectedAppContactEmail</contactEmail> <label>ConnectedAppLabelName</label> <version>ConnectedAppVersion</version> </ConnectedApp>
إذا اتبعت الخطوات نفسها بالضبط، وفشلت أثناء إنشاء package version مع الخطأ التالي
ERROR running force:package:version:create: <ConnectedAppName>: Installing an app (<ConnectedAppName>) that has been deleted.
صدقني، مررنا بذلك. ستحتاج إلى إنشاء package جديدة أولا، ثم إنشاء package version جديدة. للتحقق من ذلك قبل البدء، أنشئ مشروع dx تجريبيا جديدا وأنشئ package جديدة، وأضف connected app نفسها التي أنشأتها مسبقا، ثم أنشئ package version وأخبرني هل نجح الأمر معك.
أخيرا، تحتاج إلى مراجعة كل الحقول والكائنات وtriggers وpermission sets وcustom metadata وغيرها، وإضافة <Namespace>__
إذا وجدت خطأ مشابها يؤدي إلى فشل تثبيت package في أي بيئة، فبدلا من قضاء ساعات وأيام في محاولة فهمه، أزل Quick Actions القياسية من layout ثم أعد إضافتها بعد تثبيت package بنجاح.
(ObjectName-Layout) In field: QuickAction - no QuickAction named NewCase found
(ObjectName-Layout) In field: QuickAction - no QuickAction named NewTask found
(ObjectName-Layout) In field: QuickAction - no QuickAction named LogACall found
Component [flexipage:filterListCard] attribute [filterName]: Error retrieving filter [My_ChatterGroups] for entity [CollaborationGroup]
هناك أمر آخر يجب التفكير فيه: إذا كان source code لديك يعتمد على Salesforce built-in Components، فتأكد من منح الوصول إلى تلك المكونات كجزء من خطوات pre-deployment قبل تثبيت package version. مثل هنا، تفعيل omni channel.
Your org doesn't have access to component runtime_service_omnichannel:omniWidget.
أخيرا، إذا كانت لديك standardValueSets في source code وكان metadata الخاص بـ package version يعتمد عليها، فتأكد من نشر تلك value sets كخطوة pre-deployment قبل تثبيت package version. مثل هنا، نشر قيمة pick-list مخصصة باسم TestValue.
Component [lst:dynamicRelatedList] attribute [adminFilters]: TestValue isn't a valid picklist value.
يمكنك الرجوع إلى Metadata Coverage Documentation لمعرفة أي المكونات يمكن تضمينها في package.
احجز عرضا توضيحيا لمعرفة المزيد عن كيفية تمكين Serpent لاستراتيجية Salesforce DevOps لديك اليوم. Serpent من Tekunda يقدم أدوات ممتازة لمساعدة فرق DevOps لديك، بما في ذلك التحكم المدمج في الإصدارات، وأنظمة النشر المستمر والإصدارات، وأدوات الدمج، والعديد من أدوات الاختبار المختلفة.

Andrew Hanna

Serpent Team

Tekunda Team

Tekunda Team

Tekunda Team

Andrew Hanna