الأعضاء
`members.list` و`list_all` و`iterate` و`get` و`add` و`update` و`remove` و`grant_address` و`revoke_address`، وتوابع الدعوات إلى جانبها.
كل الدوالّ
from openemail import openemail roles = openemail.roles.list_all()support = next(role for role in roles if role['name'] == 'Support')viewer = next(role for role in roles if role['builtin'] == 'viewer') invitation = openemail.members.add({ 'email': '[email protected]', 'roleId': support['id'], 'addressIds': ['2b81de07-…'], 'access': 'member',}) people = openemail.members.list_all()sam = next(person for person in people if person['email'] == '[email protected]')member = openemail.members.get(sam['userId']) openemail.members.update(sam['userId'], {'roleId': viewer['id']}) openemail.members.grant_address(sam['userId'], { 'addressId': 'c40a95f2-…', 'access': 'viewer',})openemail.members.revoke_address(sam['userId'], 'c40a95f2-…') openemail.members.remove(sam['userId'])منحتان لكل شخص، ولا يجوز دمجهما. فـrole هو ما يجوز له فعله؛ وaddresses هو ما يجوز له فعله عليه. وعلى الاثنين أن يتوافقا: دور يحمل emails:send مع 'access': 'viewer' على invoices@ يعني شخصًا يجوز له إرسال البريد ولا يجوز له الإرسال من ذلك العنوان. والاستثناء دور يحمل addresses:all، فهو يصل إلى كل عنوان مهما سردت addresses، لأن تلك المصفوفة لا تحمل إلا المنح المباشرة، فتحقّق من permissions قبل أن تقرأها على أنها كل ما يصل إليه الشخص.
كل تابع يأخذ userId لا البريد الإلكتروني. وadd هو الاستثناء الوحيد، لأنه يدعو عنوانًا: فلا يصبح للشخص userId إلا بعد أن يقبل، ويتابع list_invitations الدعوة حتى ذلك الحين.
تعني 'implied': True أن أحدًا لم يختر الدور. فالشخص يحمل عناوين ولا يملك صف دور، فاستُنتج الدور من أوسع منحة لديه. عامِله على أنه “لم يُحسم بعد”، وupdate هو ما يحوّل الاستنتاج إلى قرار. وحتى ذلك الحين، فإن توسيع وصوله إلى العناوين يوسّع صامتًا ما يجوز له فعله.
مالك مساحة العمل هو الصف الأول، ويُعلَّم بـ'isOwner': True، بينما يرفضه add وupdate وremove بخطأ member_is_owner. ومساحة العمل غير المشتركة تبلّغ بعضو واحد لا بصفر، فاستبعد isOwner عند عدّ المقاعد.
يأخذ remove المحورين معًا، الدور وكل منحة عنوان على مساحة العمل هذه، ويبلّغ بـaddressesRevoked. أما revoke_address فهو الضيق، لمن انتقل إلى فريق آخر لا لمن غادر.
المعاملات
emailstrمطلوب- من تدعوه، بعد اقتطاع الفراغ وتحويله إلى أحرف صغيرة. ولا يلزم أن يملك حسابًا بعد: الجميع يُدعَون، ويحطّ الدور والمنح عند القبول. أما من هو في مساحة العمل أصلًا فيعطي `member_is_owner` (422).
roleIdstrمطلوب- الدور الذي سيحمله، من 1 إلى 128 حرفًا، ويجب أن يكون دورًا على مساحة العمل هذه: فالمعرّف المجهول يعطي `role_not_found` (404). ولا يمكن منح دور المالك ويعود بـ`role_immutable` (409)، لأن جعل شخص مالكًا هو نقل لمساحة العمل ولا نداء لذلك هنا.
addressIdslist[str]- العناوين التي تحملها الدعوة، بحد أقصى 64 معرّفًا طول كل منها من 1 إلى 128 حرفًا، وتُمنح عند قبولها. ويُفحص كل معرّف قبل كتابة أي شيء، فالمعرّف الذي ليس عنوانًا على مساحة العمل هذه يرفض النداء كله بـ422 `member_not_found` ولا يُرسل شيء. ودعوة العنوان نفسه مجددًا خلال عشر دقائق تعطي 409 `invitation_too_soon`.
accessLiteral['member', 'viewer']- ما يجوز له فعله بكل معرّف في `addressIds`: `member` يقرأ العنوان ويرسل باسمه، و`viewer` يقرأه فقط. والافتراضي `member`، وهو المستوى الذي استخدمته لوحة التحكم ومسار المشاركة الأقدم دائمًا، فيعني النداء نفسه الشيء نفسه من سكربت ومن شاشة؛ وامنح مزيجًا بمناداة `grant_address` بعد ذلك للعناوين المختلفة.
الاستجابة
objectLiteral['member']- دائمًا `member`. وتجيب عملية الإزالة بالقيمة نفسها، مع `userId` الخاص به و`'deleted': True` و`addressesRevoked`، دون أي من الحقول الأخرى أدناه.
userIdstr- معرّف حسابه، وهو المقبض الذي يأخذه كل نداء عضو آخر في المسار: get وupdate وremove ونداءا العناوين كلاهما. وإضافة شخص هي النداء الوحيد الذي يعمل انطلاقًا من بريد إلكتروني بدل ذلك، لأن من يضيف زميلًا يعرف عنوانه لا معرّفه.
emailstr- البريد الإلكتروني في حسابه، مُعادًا كما يخزّنه ذلك الصف. ولا يكتبه هذا المورد أبدًا، وتحويل الأحرف إلى صغيرة في `add` يسري على العنوان الذي ترسله للبحث لا على ما يعود. وبعد المالك، تُرتَّب قائمة الأعضاء بحسبه لا بحسب وقت الانضمام، لأن القائمة تُقرأ للعثور على شخص بعينه لا لرؤية ما تغيّر.
namestr | None- اسمه الظاهر، مأخوذًا من حسابه حيث العمود NOT NULL. وقيمة `None` في النوع دفاعية لا حالة شوهد هذا API ينتجها. وهو يخصّه هو لا مساحة العمل، فلا شيء في هذا المورد يمكنه ضبطه.
imagestr | None- صورته الرمزية، مأخوذة من حسابه، وتكون null حين لم يضبط واحدة.
role.idstr | None- معرّف الدور الذي يحمله، أو null حين لم يختره أحد. انظر `implied`. وقيمة null هنا هي الحالة الوحيدة التي يبلّغ فيها `role` عن استنتاج لا عن قرار اتخذه أحد.
role.namestr- اسم الدور. وبالنسبة إلى عضو ضمني هو اسم القالب المدمج الذي حُلّ إليه وصوله، لا صف على مساحة العمل هذه.
role.builtinLiteral['owner', 'admin', 'member', 'viewer', 'developer', 'billing'] | None- أي دور مدمج هو هذا الدور، أو null لدور مخصص. ولا تظهر `owner` إلا على صف المالك نفسه، إلى جانب `'isOwner': True`؛ وإسناد ذلك الدور إلى أي شخص يُرفض بـ`role_immutable` (409).
isOwnerbool- تكون true على صف واحد بالضبط، وهو الحساب الذي تُفهرَس عليه مساحة العمل. وهو يحمل كل صلاحية مهما قال صف دوره، ويتصدّر الترتيب، ويرفضه `add` و`update` و`remove` جميعًا بـ`member_is_owner`. استبعده عند عدّ المقاعد.
impliedbool- تكون true حين يملك هذا الشخص منح عناوين ولا يملك صف عضوية، فيكون دوره مستنتَجًا لا مختارًا: فأي منحة `member` تُحَل إلى دور Member المدمج، وإلا فإلى Viewer. ولا تكون true للمالك أبدًا. اعرضها بوصفها “مستنتَج من الوصول”. وإلى أن يحوّل طلب PATCH الاستنتاج إلى قرار، فإن توسيع وصوله إلى العناوين يوسّع صامتًا ما يجوز له فعله.
permissionslist[Permission]- صلاحيات الدور مسطَّحةً على العضو، فتجيب قراءة واحدة عن سؤال “هل يجوز له؟” دون جلب الدور. وبالنسبة إلى عضو ضمني تأتي الصلاحيات من القالب المدمج لا من صف الدور في مساحة العمل هذه، فتحرير دور Member المدمج لا يغيّر ما يحمله عضو ضمني.
addresseslist[MemberAddressResource]- العناوين التي أُعطيت له، مرتَّبةً بحسب العنوان، ولكل منها مستوى وصوله. وتكون فارغة لمن يحمل دورًا بلا منح، وهو شكل العضو الجديد إلى أن يُمنح عنوانًا، وهو الإخفاق الصحيح ما دمت لم تحسم بعد ما ينبغي له أن يراه.
addresses[].addressIdstr- معرّف العنوان، وهو ما يأخذه `grant_address` و`revoke_address`. ويُرفض في كليهما معرّف ليس عنوانًا على مساحة العمل هذه، بدل الإبلاغ عن سحب لم يحدث.
addresses[].addressstr- العنوان الكامل، بأحرف صغيرة، مُعاد بناؤه من جزئه المحلي ونطاقه.
addresses[].accessLiteral['member', 'viewer']- ما يجوز له فعله بهذا العنوان وحده: `member` يقرأه ويرسل باسمه، و`viewer` يقرأه فقط. وعلى هذا والدور معًا أن يسمحا بالإرسال قبل أن يحدث، فدور يحمل `emails:send` فوق منحة `viewer` لا يرسل من أي عنوان؛ والعمود المخزَّن اسمه `role`، وقد أُعيدت تسميته هنا كي لا يحمل كائن واحد حقلَي `role` مسحوبين من مفردتين مختلفتين.
createdAtstr | None- وقت كتابة صف العضوية الخاص به، بصيغة ISO-8601، ويكون null حين لا يوجد صف عضوية أصلًا. وذلك الـnull يصف الفئة نفسها التي تصفها `'implied': True`: أشخاص يحملون عناوين من قبل وجود الأدوار ولم يمنحهم أحد دورًا منذ ذلك الحين.
الدعوات
from openemail import openemail waiting = openemail.members.list_all_invitations() for invitation in waiting: if invitation['expired']: openemail.members.resend_invitation(invitation['id']) openemail.members.revoke_invitation('winv_6bb640f5b99e47deb758f1f5')يجيب add بدعوة، وهذه هي النداءات التي تتابعها: list_invitations وlist_all_invitations وiterate_invitations تقرأ الدعوات التي لم يقبلها أحد بعد، وresend_invitation يعيد إرسال إحداها برابط جديد وأربعة عشر يومًا إضافية، وrevoke_invitation يسحبها. والدعوة المنتظرة لا تمنح شيئًا حتى تُقبل.
يرفض resend_invitation العنوان نفسه مرتين خلال عشر دقائق بـ409 invitation_too_soon، ويرفض revoke_invitation الدعوة التي قُبلت أولًا بـ409 invitation_accepted.
رموز التحقق
تطلب add وupdate وremove وgrant_address وrevoke_address من رمز وصول OAuth رمز تحقق قبل أن تغيّر أي شيء، أما resend_invitation وrevoke_invitation فلا. ويرفع الاستدعاء OpenEmailApiError قيمة is_step_up_required فيه True: اطلب رمزًا بـ security.begin_step_up()، وافحص الرمز الذي يعطيك إياه الشخص بـ security.verify_step_up({'code': ...})، ثم أعد الاستدعاء. والتحقق الواحد يسري 60 دقيقة، ولا يُطلب ذلك من مفتاح API أبدًا.
المرجع
members.list()المرجع الكاملmembers.list_all()المرجع الكاملmembers.iterate()المرجع الكاملmembers.get()المرجع الكاملmembers.add()المرجع الكاملmembers.update()المرجع الكاملmembers.remove()المرجع الكاملmembers.grant_address()المرجع الكاملmembers.revoke_address()المرجع الكاملmembers.list_invitations()المرجع الكاملmembers.list_all_invitations()المرجع الكاملmembers.iterate_invitations()المرجع الكاملmembers.resend_invitation()المرجع الكاملmembers.revoke_invitation()المرجع الكامل