الأدوار
`roles.list` و`get` و`create` و`update` و`delete` و`listPermissions`.
كل التوابع
const roles = await openemail.roles.list()const role = await openemail.roles.get('role_…') const support = await openemail.roles.create({ name: 'Support', description: 'Answers the shared inboxes and nothing else.', permissions: ['emails:send', 'threads:write', 'labels:write'],}) console.log(support.permissions) await openemail.roles.update(support.id, { permissions: [...support.permissions, 'templates:read'],}) await openemail.roles.delete(support.id, { reassignTo: 'role_…' }) const vocabulary = await openemail.roles.listPermissions()يحمل support.permissions ستة مدخلات لا ثلاثة: فـemails:send يجلب emails:read، وthreads:write يجلب threads:read، وlabels:write يجلب labels:read. اقرأ القائمة بعد الكتابة بدل افتراضها.
الدور يقول ما يجوز لشخص أن يفعله. أما العناوين التي يجوز له أن يفعله عليها فهي المحور الآخر وتقيم في openemail.members. انظر grantAddress وrevokeAddress هناك. فجملة “يجوز له إرسال البريد” وجملة “يجوز له الإرسال باسم invoices@” جملتان مختلفتان، ومساحة عمل توظّف وكيل دعم ثانيًا تغيّر الثانية دون المساس بالأولى.
تفرّع على editable وdeletable لا على اسم builtin. فكلاهما false للمالك وحده، الذي قائمته هي “كل صلاحية، بما فيها ما سيُخترع العام المقبل” وتُحسب ولا تُخزَّن؛ وكل دور آخر يجيب true عليهما، بما في ذلك الأدوار الخمسة التي تُبذَر بها مساحة العمل. والدور الذي أعاد أحدهم تسميته يظل يجيب عنهما بصدق، ولم يعد اسمه يخبرك بشيء.
يستبدل update قائمة الصلاحيات كاملةً. ولا يوجد نداء لمنح صلاحية واحدة، فاقرأ الدور، وغيّر المدخل الذي تقصده، وأعد إرسالها جميعًا. وإرسال صلاحية واحدة يترك الدور حاملًا تلك الصلاحية وحدها، مع ما تستلزمه.
يحتاج delete إلى reassignTo فور أن يحمل أحدٌ الدور، وهو ينتقل كمعامل استعلام لأن متن طلب DELETE تُسقطه عدة بيئات تشغيل وعدد من الوسطاء. وتبلّغ النتيجة عن reassigned وkeysReassigned كلٍّ على حدة، فيتمكن السكربت من تسجيل ما فعله لا ما طلبه.
إن listPermissions() هو GET /roles/permissions، مسار ثابت يقع تمامًا حيث يقع معرّف الدور. ويثبّته العميل في شفرته بدل تمرير السلسلة عبر get، فطلب دور اسمه فعلًا “permissions” يطلب دورًا ويحصل على 404، وهو الجواب الصادق عمّا كُتب. وتعلّم scope: false المدخلات التي لا يمكن لأي مفتاح أن يحملها أبدًا.
الدور هو سقف المفتاح
المفتاح الصادر مقابل دور يجوز له ما يقع في تقاطع نطاقاته الخاصة مع صلاحيات ذلك الدور، ويُحسم ذلك لكل طلب عند الحدود. فتضييق دور يسحب صلاحيات مفاتيحه فورًا دون تدوير أي منها، والمفتاح بلا دور ليس له سقف إطلاقًا، وهو ما يجعل الدور null أوسع حالة يمكن للمفتاح أن يكون فيها لا أضيقها.
ولهذا أيضًا يصرّ roles.delete على وجهة تُنقل إليها المفاتيح. فتيتيمها كان سيسقط سقفها بالكامل، ويرفع صامتًا كل اعتماد كان الدور يحدّه.
يبلّغ GET /keys/self وGET /ping عن roleId وgrantedScopes إلى جانب scopes الفعلية، وهكذا يُجاب عن “مفتاحي يحمل emails:send ومع ذلك أتلقى insufficient_scope”: فكل ما هو في grantedScopes وغائب عن scopes قد أخذه الدور. ويعيد openemail.me.get() وopenemail.me.ping() كليهما، بأنواع محدَّدة.
المعاملات
namestringمطلوب- ما تسمّي به مساحة العمل الدور: من 1 إلى 48 حرفًا، ويُقتطع الفراغ قبل التخزين. والأسماء فريدة في كل مساحة عمل دون اعتبار لحالة الأحرف، فيُرفض دور ثانٍ باسم “Support” بـ`role_name_taken` (409) بدل إنشائه إلى جانب الأول.
descriptionstring- جملة تقول ما الدور لأجله، يُقتطع فراغها وطولها 240 حرفًا كحد أقصى. والسلسلة التي تصير فارغة بعد الاقتطاع تُخزَّن كـnull، فوصف مكوَّن من فراغات يعود null لا كما أرسلته.
permissionsPermission[]مطلوب- ما يمنحه الدور، مسحوبًا من المفردات التي يقدّمها `listPermissions()`؛ وسلسلة ليست منها تعطي 422 على `permissions` بدل إسقاطها صامتةً، فيُبلَّغ عن الخطأ المطبعي بدل أن يكلّفك بعد ظهيرة كاملة. وتُوسَّع القائمة عند الدخول (فـ`templates:write` يخزّن `templates:read` إلى جانبه)، ويُزال تكرارها وتُعاد إلى الترتيب المعياري، فاقرأ القائمة المخزَّنة من الاستجابة بدل افتراض أنها التي أرسلتها.
الاستجابة
object'role'- دائمًا `role`. ويجيب شاهد الحذف بالقيمة نفسها، مع `id` الدور و`deleted: true` وعدّادَي إعادة الإسناد، دون أي من الحقول الأخرى أدناه.
idstring- معرّف الدور. وهو ما يسمّيه `roleId` الخاص بالعضو، وما يشير إليه سقف مفتاح API، وما يأخذه `reassignTo` عند حذف هذا الدور.
namestring- اسم الدور في مساحة العمل، مقتطَع الفراغ وفريد دون اعتبار لحالة الأحرف. ويمكن إعادة تسمية كل دور إلا دور المالك، بما في ذلك الأدوار المبذورة (فـ`builtin` يقول من أين جاء الصف، لا بأي اسم يجب أن يبقى)، فلا تقرأ “Admin” على أنه وعد بما يحمله الدور. والاسم الذي يستجيب له دور آخر أصلًا يعطي `role_name_taken` (409، `param: "name"`)؛ وإعادة تسمية المالك تعطي `role_immutable` (409)، كأي تعديل آخر عليه.
descriptionstring | null- الجملة الواصفة للدور، أو null حين لم تُعطَ. والإدخال الفارغ يُخزَّن كـnull في الإنشاء والتحديث معًا، فلا يكون هذا الحقل سلسلة فارغة أبدًا.
permissionsPermission[]- كل ما يمنحه الدور، موسَّعًا مسبقًا وبالترتيب المعياري لا بالترتيب الذي كتبه أحد. وذلك الترتيب حامل للبنية: فدوران يحملان الصلاحيات نفسها يتساويان عند المقارنة كـJSON، وهو ما يتيح لشاشة الإعدادات أن تقارنهما لتقرر ما إذا كان زر الحفظ مفعّلًا.
builtin'owner' | 'admin' | 'member' | 'viewer' | 'developer' | 'billing' | null- من أي من الأدوار الستة المبذورة جاء هذا الصف، أو null لدور كتبته مساحة العمل بنفسها. وهو يسجّل البذرة لا الحالة: فالدور المبذور يُعاد تسميته وتُغيَّر صلاحياته ويُحذف كأي دور آخر. تفرّع على `editable` و`deletable` لا على هذا الحقل. فالدور الذي سماه أحدهم “Admin” ليس بالضرورة الدور المبذور، والمبذور قد لم يعد يحمل ذلك الاسم.
editableboolean- يُحسب على أنه `builtin !== 'owner'`، فيكون false لدور المالك وحده ويُرفض كل طلب PATCH على ذلك الدور بـ`role_immutable` (409). وكل دور آخر قابل للتحرير بالكامل (الاسم والوصف والصلاحيات)، بما في ذلك الأدوار الخمسة التي تُبذَر بها مساحة العمل.
deletableboolean- يُحسب على أنه `builtin !== 'owner'`: فهو false لدور المالك وحده الذي يعود بـ`role_undeletable` (409)، وtrue لكل دور آخر بما فيها المبذورة. تحقّق منه قبل عرض الزر لا بعد الرفض، علمًا أن دورًا ما زال أحدهم يحمله يحتاج أيضًا إلى `reassignTo`، وإلا كان الحذف `role_in_use` (409).
membersnumber- كم شخصًا يحمل هذا الدور، محسوبًا من صفوف أعضاء مساحة العمل. والمالك ليس بينهم: فليس له صف عضوية ولا يمكن منحه دورًا، فيبلّغ دور Owner بصفر حاملين رغم أن قائمة الأعضاء تعرضه.
apiKeysnumber- كم مفتاح API حي يحدّه هذا الدور؛ وتُستثنى المفاتيح الملغاة من العدّ، رغم أن الحذف يعيد توجيه كل صف مفتاح يشير إلى الدور، بما فيها الملغاة. وهي الفئة الثانية التي يجب نقلها قبل أن يذهب الدور، والفئة التي لا ينتبه إليها أحد: فالمفاتيح برامج، والبرنامج لا يشتكي.
createdAtstring- وقت كتابة صف الدور، بصيغة ISO-8601. وتُبذَر الصفوف المدمجة بتكاسل أول مرة يحتاجها شيء ما، كقراءة قائمة الأدوار أو إنشاء دور أو شاشة مفاتيح API، لا عند إنشاء مساحة العمل، فطابع الوقت لدور مدمج هو لحظة وصول ذلك الطلب الأول لا لحظة إنشاء مساحة العمل.
updatedAtstring- وقت آخر تغيير للدور، بصيغة ISO-8601. ويحرّكه كل طلب PATCH مقبول، بما في ذلك طلب يضبط حقلًا على القيمة التي كان يحملها أصلًا.