איך להשתמש במתודה forName של המחלקה Type כדי לוודא הימצאות Namespace בעת הרצת מתודות בדיקה בסביבות פיתוח שונות

ליאור נכתב על ידי ליאור לביא, עודכן בתאריך 17/07/2023

כאשר אנחנו מפתחים חבילות עם Namespaces, אחד הנושאים שאנחנו צריכים לתת עליהם את הדעת הוא ההשפעות של שימוש ב-Namespace כאשר אנחנו משתמשים ב-Strings כדי לפנות לרכיבי Metadata. מצד אחד זאת נקודת חוזקה של קוד דינמי שיכול לקבל References לרכיבי Metadata בשמם, אבל מצד שני, כאשר נרצה להריץ את הקוד שלנו בסביבות בהן ה-Metadata שלנו לא מקבל אוטומטית קידומת Namespace, כמו Scratch orgs ללא Namespace למשל, ההפניות שלנו ל-Metadata באמצעות Strings עלולות לגרום לבעיות, כמו שבירת Unit tests.

בוא נסתכל על הדוגמה הבאה:

String recordString = '{"ACME__Custom_Field_1__c":"Some text","ACME__Custom_Field_2__c":"More text","ACME__Custom_Field_3__c":"Even more text!"}';
Custom_Metadata_Object__mdt record = (Custom_Metadata_Object__mdt) JSON.deserialize(recordString, Custom_Metadata_Object__mdt.class);

הקוד בדוגמה יוצר רשומה של אובייקט Metadata בשם Custom_Metadata_Object__mdt. יצירת הרשומה מבוצעת על ידי קריאה למתודה JSON.deserialize עם מחרוזת המייצגת את הרשומה בפורמט JSON ואת ה-Class של ה-Type. שים לב שהמתודה ב-JSON.deserialize מחזירה ערך מסוג Object, ולכן נצטרך לבצע Cast, או המרה מפורשת, ל-Custom_Metadata_Object__mdt.

מה שחשוב לשים לב אליו בדוגמה למעלה הוא שהשדות הכתובים בתוך המחרוזת recordString מתחילים כולם עם ה-Namespace ACME. הדוגמה הזאת תעבוד בצורה תקינה בסביבות בהן ה-Namespace ברירת המחדל הוא ACME, כמו Scratch org לפיתוח בהן ACME הוגדר כ-Namespace, או בסביבות בלי Namespace או עם Namespace ברירת מחדל שונה, בהן הותקנה חבילה עם ה-Namespace הזה והאובייקט Custom_Metadata_Object__mdt נכלל בה.

ברגע שנרצה להריץ את הדוגמה הזאת בסביבה בה ה-Namespace שונה מ-ACME וביצענו אליה Push או Deploy ישירות מהפרויקט שלנו ב-Visual Studio Code, ולא התקנו את החבילה עצמה, נתקל בבעיה - בגלל שה-Namespace ACME לא מוגדר בסביבה, הרשומה שיצרנו אומנם תשמר בזיכרון ה-Runtime, אבל אם ננסה להשתמש בה נמצא את עצמנו עם שדות שאינם באמת קיימים בסביבה, ובכל זאת מוגדרים כחלק מהרשומה שיצרנו עבור האובייקט Custom_Metadata_Object__mdt.

דרך פשוטה להימנע מהמצב הזה מלכתחילה הוא להשתמש במתודה JSON.deserializeStrict() במקום המתודה JSON.deserialize(). מתודה זאת הייתה נכשלת בזמן ריצה ומציגה הודעת שגיאה לפיה השדות עם השמות הכוללים את ה-Namespace ACME אינם קיימים על האובייקט.

הפער הזה, בין השדות שקיימים ברשומה שיצרנו ב-Runtime, ובין השדות הקיימים באמת בסביבה, אשר אינם כוללים את ה-Namespace ACME בשמם, עלול לגרום לבדיקות שכתבנו להיכשל.

אתה עשוי לשאול את עצמך: למה בעצם אנחנו מתעכבים פה על המקרה של בדיקות בכל מה שקשור לשימוש ב-Namespace?
התשובה היא שאחד המקומות בהן השימוש ב-Namespace מופיע הוא כשיוצרים מופע של רשומה בזמן ריצה במטרה לדמות רשומות Custom metadata כחלק משימוש ב-Dependency injection כדי לבצע Unit tests. אם אתה לא מכיר את המונח Dependency injection או לא בטוח למה הוא נדרש דווקא בעבודה עם Custom metadata במסגרת בדיקות, אל חשש! אני מתכנן לכתוב פוסט מפורט ספציפית בנושא 😊. עד אז, אתה מוזמן להמשיך לקרוא את הפוסט הנוכחי, למקרה ותתקל במקרים נוספים בהם שימוש במחרוזות ליצירת רשומות או פניה לשדות או אובייקטים עם Namespace גורמת לך לבעיות.

פתרון

דרך אחת להתמודד עם הבעיה שלנו הוא שימוש במתודה forName של המחלקה Type כדי לבדוק האם קיים Type עבור מחלקה תחת Namespace ספציפי. אני כותב פה "מחלקה", אבל המתודה עובדת גם עבור Custom metadata.
לקריאה נוספת על המחלקה Type, המתודות שלה ו-Use cases, לחץ פה.

דוגמה

String namespace = Type.forName('acme', 'Custom_Metadata_Object__mdt') != null ? 'ACME__' : '';
Custom_Metadata_Object__mdt record = (Custom_Metadata_Object__mdt) JSON.deserialize('{"' + namespace + 'Custom_Field_1__c":"Some text"}', Custom_Metadata_Object__mdt.class);

מה שהמתודה Type.forName עושה הוא לבדוק האם תחת ה-Namespace Acme (על ה-Namespace להיכתב באותיות קטנות) קיימת מחלקה או אובייקט בשם Custom_Metadata_Object__mdt. אם כן, הוא שומר למשתנה namespace את הערך ACME__, ואם לא, הוא שומר מחרוזת ריקה. לאחר מכן, אנחנו יכולים להשתמש במשתנה namespace כדי להגדיר במחרוזת ה-JSON שלנו האם השדות צריכים להכיל קידומת Namespace או לא. כך נפתרת עבורנו הבעיה של בירור האם הסביבה מכילה את ה-Namespace שלנו או לא.

וזה הכול לפוסט זה! יצירת רשומות Metadata בזמן ריצה היא בנושא שלא בהכרח הרבה מתכנתי Apex מתמודדים אתו ביום-יום, אבל חשבתי שכדאי להציג אותו מאחר ו-Metadata ו-Namespaces הם נושאים מרכזיים בפיתוח חבילות וגרסאות, והשימוש ב-Type.forName() הוא כלי מועיל במיוחד לפתרון בעיה שעלולה להפוך למתסכלת מאוד.