סכימות — לקרוא לפני שכותבים
הקריאה הראשונה של כל אינטגרציה, לפני ה‑CRUD ולפני ההתחברות.
כל לקוח הוא אפליקציה נפרדת שהתחילה מאותו בסיס מוצר והותאמה: שדות נוספו, שדות הוסרו, שדות באותו שם קיבלו טיפוס אחר. סכימה שהועתקה מלקוח אחד משקרת אצל השני.
מה חוזר
Section titled “מה חוזר”curl -sS "https://api.mbapps.co.il/parse/schemas/Accounts" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-API-Key: $API_KEY"const res = await fetch('https://api.mbapps.co.il/parse/schemas/Accounts', { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import requests
res = requests.get( "https://api.mbapps.co.il/parse/schemas/Accounts", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, timeout=30,)<?php$ch = curl_init('https://api.mbapps.co.il/parse/schemas/Accounts');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);{ "className": "Accounts", "fields": { "objectId": { "type": "String" }, "createdAt": { "type": "Date" }, "updatedAt": { "type": "Date" }, "ACL": { "type": "ACL" }, "createdBy": { "type": "Pointer", "targetClass": "_User" }, "updatedBy": { "type": "Pointer", "targetClass": "_User" }, "Name": { "type": "String" }, "PhoneNumber": { "type": "String" }, "Email": { "type": "String" }, "IsAccount": { "type": "Boolean" }, "StatusId": { "type": "Pointer", "targetClass": "AccountStatuses" }, "LeadStatusId": { "type": "Pointer", "targetClass": "LeadStatuses" }, "LeadSourceId": { "type": "Pointer", "targetClass": "LeadSource" }, "Comment": { "type": "HTML/XML" }, "array_languages_Pointer_Languages": { "type": "Array" } }, "classLevelPermissions": { "find": { "role:Sales": true }, "get": {}, "create": {} }, "triggers": [], "timeline": { "displayFields": { "Name": "AccountName" }, "index": { "objectId": "AccountId" } }}הדוגמה מקוצרת: Accounts במערכת חדשה מחזירה 40 שדות, ולקוחות מוסיפים משלהם.
| מפתח | מה הוא אומר |
|---|---|
className |
שם הטבלה — הערך שתשלחו בנתיב |
fields |
מפה של שם שדה → הגדרה |
fields.<X>.type |
הטיפוס. מכתיב את הפורמט ב‑JSON — ראו סוגי נתונים |
fields.<X>.targetClass |
ל‑Pointer ו‑Relation בלבד: לאיזו טבלה השדה מצביע |
fields.<X>.dictionary |
התווית בעברית של השדה. ב‑Get-Schema של שכבת ה‑MCP היא חוזרת תמיד; ב‑REST רק כשמוסיפים ?withDictionary=true. ראו למטה |
classLevelPermissions |
הרשאות הטבלה. מפה ריקה = אף אחד לא רואה כלום |
triggers |
מערך הגדרות האוטומציה של הטבלה — ראו למטה |
timeline |
הגדרות ציר הזמן. מופיע רק בטבלאות שהרישום שלהן דלוק — 11 טבלאות מהקופסה (Accounts, Contacts, Sales, PriceQuotes, Cases, Tasks, Activities, Notes, Conversations, Emails, SMS) |
בלי שם טבלה בנתיב (GET /parse/schemas) חוזרת רשימת כל המחלקות: 111 במערכת חדשה, כמעט 200 בסביבה מותאמת. שאלו טבלה ספציפית כשאתם יודעים מה אתם מחפשים. שתי הצורות דורשות Master Key או API Key; עם X-Parse-Application-Id בלבד חוזר 403 {"error":"unauthorized: master key is required"}.
targetClass — המפתח לכל Pointer
Section titled “targetClass — המפתח לכל Pointer”בלי targetClass אי אפשר לכתוב Pointer תקין: הפורמט ב‑JSON דורש את className.
// מהסכימה"LeadSourceId": { "type": "Pointer", "targetClass": "LeadSource" }
// ולכן הכתיבה ב-REST היא{ "LeadSourceId": { "__type": "Pointer", "className": "LeadSource", "objectId": "vOZLqOWnQf" } }הצעד הבא: שליפת טבלאות ה‑lookup ובניית מיפוי תווית → objectId.
curl -sS -G "https://api.mbapps.co.il/parse/classes/LeadSource" \ -H "X-Parse-Application-Id: $APP_ID" -H "X-Parse-API-Key: $API_KEY" \ --data-urlencode 'keys=Name' --data-urlencode 'limit=200'const params = new URLSearchParams({ keys: 'Name', limit: '200' });const res = await fetch(`https://api.mbapps.co.il/parse/classes/LeadSource?${params}`, { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, },});import requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/LeadSource", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, }, params={"keys": "Name", "limit": 200}, timeout=30,)<?php$params = http_build_query(['keys' => 'Name', 'limit' => 200]);$ch = curl_init("https://api.mbapps.co.il/parse/classes/LeadSource?$params");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . $appId, 'X-Parse-API-Key: ' . $apiKey, ],]);$response = curl_exec($ch);curl_close($ch);dictionary — לבנות ממשק בעברית בלי לתרגם ידנית
Section titled “dictionary — לבנות ממשק בעברית בלי לתרגם ידנית”התווית העברית נשמרת בטבלת המערכת _Dictionary (tblName, field, value). מכאן אפשר לייצר טופס בעברית ישירות מהסכימה, בלי לקודד תרגומים ובלי לצאת מסנכרון כשהלקוח משנה תווית. השאלה היא מאיזו שכבה קוראים:
| השכבה | איפה התווית |
|---|---|
MCP — Get-Schema |
inline בתוך הגדרת השדה: "JoinDate": { "type": "Date", "dictionary": "תאריך הצטרפות" } |
REST — GET /parse/schemas/<Table>?withDictionary=true |
inline, אותו מבנה בדיוק |
REST — GET /parse/schemas/<Table> |
אינה חוזרת. בלי הפרמטר מוחזר "JoinDate": { "type": "Date" } בלבד, גם כששורת _Dictionary קיימת |
curl -sS "https://api.mbapps.co.il/parse/schemas/Accounts?withDictionary=true" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-Master-Key: $MASTER_KEY"הפרמטר תקף גם על רשימת כל המחלקות (GET /parse/schemas?withDictionary=true), ודורש Master Key או API Key ככל קריאת סכימה.
כשצריך את התוויות כמפה נפרדת, ולא מוטמעות בסכימה, שלפו את _Dictionary ומזגו לפי tblName + field:
// טופס בעברית מתוך REST: סכימה + _Dictionaryconst [schemaRes, dictRes] = await Promise.all([ fetch('https://api.mbapps.co.il/parse/schemas/Accounts', { headers: H }), fetch('https://api.mbapps.co.il/parse/classes/_Dictionary?limit=1000&where=' + encodeURIComponent(JSON.stringify({ tblName: 'Accounts' })), { headers: H }),]);const { fields } = await schemaRes.json();const labels = Object.fromEntries((await dictRes.json()).results.map(r => [r.field, r.value]));
const SYSTEM = new Set(['objectId', 'createdAt', 'updatedAt', 'ACL', 'createdBy', 'updatedBy']);
const formFields = Object.entries(fields) .filter(([name]) => !SYSTEM.has(name)) .map(([name, def]) => ({ name, label: labels[name] ?? name, // תווית עברית, ואם אין — שם השדה type: def.type, target: def.targetClass ?? null, // צריך שליפת lookup }));triggers — למה כתיבה “עושה דברים מוזרים”
Section titled “triggers — למה כתיבה “עושה דברים מוזרים””תשובת הסכימה הפרטנית נושאת גם מערך triggers: הגדרות האוטומציה נשמרות יחד עם הסכימה של הטבלה.
נקודת האבחון הראשונה כשכתיבה מייצרת התנהגות לא צפויה: עדכון סטטוס ששולח מייל, יצירת רשומה שיוצרת שלוש נוספות, שדה שמשנה את עצמו מיד אחרי הכתיבה.
שינוי סכימה — לא דרך REST גולמי
Section titled “שינוי סכימה — לא דרך REST גולמי”טבלה שנוצרה ב‑REST גולמי נולדת חסרת הרשאות. ה‑CLP שלה ריק לחלוטין ({"find":{},"get":{},"create":{},"update":{},"delete":{}}), ומכאן “השדות מופיעים ריקים” ו“המשתמש לא רואה כלום”, התקלה הנפוצה ביותר אחרי הקמה. טבלה שנוצרה ב‑Create-Table מקבלת את ברירת המחדל שלמעלה, וגם אותה פותחים לתפקידים הרלוונטיים מיד אחרי היצירה.
מה לא חוזר בסכימה
Section titled “מה לא חוזר בסכימה”| מה | איפה כן |
|---|---|
| אילו שדות מאונדקסים לחיפוש טקסט | אין נקודת קצה. הבדיקה היחידה: $text שמחזיר 500 IndexNotFound = אין אינדקס |
| ערכי טבלאות ה‑lookup | שליפה רגילה מהטבלה עצמה |
| מבנה עמודים וטפסים | שכבת ה‑site, לא Parse — כלי העמודים בלבד |
_Role ו‑_Session |
Get-Schema של MCP מחזיר עליהן {}; ב‑REST עם Master Key הן חוזרות מלאות |
הרשאות הטבלה כן חוזרות, במפתח classLevelPermissions.
סקריפט תיאור טבלה
Section titled “סקריפט תיאור טבלה”#!/usr/bin/env python3"""describe_table.py <TableName> — מה יש בטבלה הזו, בעברית ובאנגלית."""import os, sys, requests
APP_ID = os.environ["MB_APP_ID"]API_KEY = os.environ["MB_API_KEY"]TABLE = sys.argv[1]
r = requests.get( f"https://api.mbapps.co.il/parse/schemas/{TABLE}", headers={"X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY}, timeout=30,)r.raise_for_status()schema = r.json()
SYSTEM = {"objectId", "createdAt", "updatedAt", "ACL", "createdBy", "updatedBy"}fields = schema["fields"]print(f'{schema["className"]} — {len(fields)} שדות\n')
for name, d in sorted(fields.items()): if name in SYSTEM: continue kind = d["type"] if d.get("targetClass"): kind += " -> " + d["targetClass"] print(f' {name:<38} {kind:<30} {d.get("dictionary", "")}')
print(f'\ntriggers: {len(schema.get("triggers", []))}')המשך מכאן
Section titled “המשך מכאן”- סוגי נתונים — מהטיפוס שקראתם אל פורמט הכתיבה
- מפת הטבלאות · עבודה עם רשומות
- יומנים וראיות — מה עשה הטריגר שראיתם בסכימה
- שגיאות ופתרון תקלות — “200 אבל השדה ריק”