1. نظرة عامة
تهدف سلسلة البرامج التعليمية حول Serverless Migration Station (برامج تعليمية ذاتية السرعة وعملية) ومقاطع الفيديو ذات الصلة إلى مساعدة مطوّري الحوسبة بدون خادم على Google Cloud في تحديث تطبيقاتهم من خلال إرشادهم خلال عملية نقل واحدة أو أكثر، مع التركيز بشكل أساسي على التخلّي عن الخدمات القديمة. يؤدي ذلك إلى زيادة قابلية نقل تطبيقاتك ويمنحك المزيد من الخيارات والمرونة، ما يتيح لك الدمج مع مجموعة أكبر من منتجات Cloud والوصول إليها، كما يسهّل عليك الترقية إلى أحدث إصدارات اللغة. على الرغم من أنّ هذه السلسلة تركّز في البداية على أوائل مستخدمي Cloud، وخاصةً مطوّري App Engine (البيئة العادية)، إلا أنّها واسعة النطاق بما يكفي لتشمل منصات أخرى بلا خادم، مثل Cloud Functions وCloud Run، أو في أي مكان آخر إذا كان ذلك منطبقًا.
الغرض من هذا الدرس التطبيقي حول الترميز هو توضيح كيفية نقل البيانات من واجهة برمجة التطبيقات/الخدمة الخاصة بمستخدمي App Engine إلى Cloud Identity Platform (GCIP) لمطوّري تطبيقات Python 2 على App Engine. هناك أيضًا عملية نقل ضمنية من App Engine NDB إلى Cloud NDB للوصول إلى Datastore (يتم تناولها بشكل أساسي في الوحدة التدريبية 2 الخاصة بعملية النقل)، بالإضافة إلى ترقية إلى الإصدار 3 من Python.
تتناول الوحدة 20 كيفية إضافة استخدام واجهة برمجة التطبيقات Users API إلى نموذج التطبيق في الوحدة 1. في هذه الوحدة، ستأخذ تطبيق الوحدة 20 المكتمل وتنقل استخدامه إلى Cloud Identity Platform.
ستتعرَّف على كيفية إجراء ما يلي:
- استبدِل استخدام خدمة "مستخدمو App Engine" بـ منصة Cloud Identity
- استبدِل استخدام App Engine NDB بـ Cloud NDB (راجِع أيضًا الوحدة 2).
- إعداد موفّري هوية مختلفين للمصادقة باستخدام Firebase Auth
- استخدام Cloud Resource Manager API للحصول على معلومات إدارة الهوية وإمكانية الوصول في المشروع
- استخدام مدير SDK في Firebase للحصول على معلومات المستخدم
- نقل نموذج التطبيق إلى Python 3
المتطلبات
- مشروع Google Cloud Platform مع حساب فوترة نشط على Google Cloud Platform
- مهارات أساسية في لغة Python
- معرفة عملية بالأوامر الشائعة على نظام التشغيل Linux
- معرفة أساسية بشأن تطوير ونشر تطبيقات App Engine
- نموذج تطبيق App Engine لوحدة 20 يعمل
استطلاع
كيف ستستخدم هذا البرنامج التعليمي؟
كيف تقيّم تجربتك مع Python؟
ما هو تقييمك لتجربة استخدام خدمات Google Cloud؟
2. الخلفية
خدمة "مستخدمو App Engine" هي نظام لمصادقة المستخدمين يمكن لتطبيقات App Engine استخدامه. توفّر هذه الخدمة ميزة "تسجيل الدخول باستخدام حساب Google" كموفّر هوية، وتوفّر روابط سهلة لتسجيل الدخول والخروج لاستخدامها في التطبيقات، وتتيح مفهوم المستخدمين المشرفين والوظائف المخصّصة للمشرفين فقط. لتحسين إمكانية نقل التطبيقات، تنصح Google Cloud بالانتقال من الخدمات المجمّعة القديمة في App Engine إلى خدمات Cloud المستقلة، مثل الانتقال من خدمة المستخدمين إلى Cloud Identity Platform، وغيرها.
تستند Identity Platform إلى مصادقة Firebase، وتضيف عددًا من ميزات المؤسسات، بما في ذلك المصادقة المتعدّدة العوامل، وتوافق الدخول المُوحَّد مع OpenID Connect وSAML، والاستخدام المتعدّد للمستأجرين، واتفاقية مستوى الخدمة بنسبة% 99.95، وغير ذلك. يتم أيضًا توضيح هذه الاختلافات في صفحة مقارنة منتجات Identity Platform و"مصادقة Firebase". يتضمّن كلا المنتجين ميزات أكثر بكثير من الوظائف التي توفّرها خدمة "المستخدمون".
يوضّح درس Module 21 التطبيقي حول الترميز كيفية تبديل مصادقة المستخدم في التطبيق من خدمة المستخدمين إلى ميزات Identity Platform التي تحاكي الوظائف الموضّحة في Module 20 بشكلٍ كبير. تتضمّن الوحدة 21 أيضًا عملية نقل البيانات من App Engine NDB إلى Cloud NDB للوصول إلى Datastore، مع تكرار عملية نقل البيانات في الوحدة 2.
على الرغم من أنّ رمز الوحدة 20 "يتم الإعلان عنه" كتطبيق نموذجي بلغة Python 2، إلا أنّ المصدر نفسه متوافق مع Python 2 و3، وسيظلّ كذلك حتى بعد نقل البيانات إلى Identity Platform (وCloud NDB) هنا في الوحدة 21. يمكنك مواصلة استخدام خدمة "المستخدمون" أثناء الترقية إلى Python 3 لأنّ عملية نقل البيانات إلى Identity Platform اختيارية. راجِع الدرس التطبيقي حول الترميز Module 17 والفيديو لمعرفة كيفية مواصلة استخدام الخدمات المجمّعة أثناء الترقية إلى أوقات تشغيل الجيل الثاني، مثل Python 3.
يتضمّن هذا الدليل التعليمي الخطوات التالية:
- الإعداد/العمل التحضيري
- تعديل الإعدادات
- تعديل الرمز البرمجي للتطبيق
3- الإعداد/العمل التحضيري
يوضّح هذا القسم كيفية تنفيذ ما يلي:
- إعداد مشروعك على السحابة الإلكترونية
- الحصول على نموذج تطبيق أساسي
- (إعادة) نشر تطبيق أساسي وإثبات صحته
- تفعيل خدمات/واجهات برمجة تطبيقات جديدة في Google Cloud
تضمن هذه الخطوات أنك تبدأ برمز برمجي صالح وجاهز لنقله إلى خدمات Cloud المستقلة.
1. إعداد المشروع
إذا أكملت درس Module 20 التطبيقي حول الترميز، أعِد استخدام المشروع (والرمز) نفسه. بدلاً من ذلك، يمكنك إنشاء مشروع جديد تمامًا أو إعادة استخدام مشروع حالي آخر. تأكَّد من أنّ المشروع يتضمّن حساب فوترة نشطًا وتطبيق App Engine مفعَّلاً. ابحث عن رقم تعريف مشروعك واحتفظ به في متناول يدك أثناء هذا الدرس التطبيقي حول الترميز واستخدِمه كلّما صادفت المتغيّر PROJ_ID.
2. الحصول على نموذج تطبيق أساسي
أحد المتطلبات الأساسية هو توفّر تطبيق يعمل على Module 20 App Engine، لذا إما أن تُكمل درس تطبيقي حول الترميز هذا (ننصحك بذلك؛ الرابط أعلاه) أو تنسخ رمز Module 20 من المستودع. سواء استخدمت رمزًا خاصًا بك أو رمزًا من رموزنا، هذه هي الخطوة الأولى ("ابدأ"). يرشدك هذا الدرس التطبيقي حول الترميز خلال عملية نقل البيانات، وينتهي برمز يشبه الرمز الموجود في مجلد مستودع الوحدة 21 ("FINISH").
- START: مجلد الوحدة 20 (Python 2)
- إنهاء: مجلدات الوحدة 21 ( Python 2 أو Python 3)
- المستودع بأكمله (لاستنساخ ملف ZIP أو تنزيله)
انسخ مجلد مستودع الوحدة التدريبية 20. يجب أن يبدو الناتج على النحو التالي، وقد يتضمّن مجلد lib إذا كنت قد نفّذت الدرس العملي حول الوحدة 20:
$ ls README.md appengine_config.py templates app.yaml main.py requirements.txt
3- (إعادة) نشر تطبيق أساسي وإثبات صحته
اتّبِع الخطوات التالية لنشر تطبيق الوحدة 20:
- احذف المجلد
libإذا كان متوفّرًا، ثم شغِّلpip install -t lib -r requirements.txtلإعادة ملئه. قد تحتاج إلى استخدامpip2إذا كان لديك الإصداران 2 و3 من Python مثبّتَين. - تأكَّد من تثبيت وإعداد أداة سطر الأوامر
gcloudومراجعة طريقة استخدامها. - إذا كنت لا تريد إدخال
PROJ_IDمع كل أمرgcloudيتم إصداره، اضبط مشروع على السحابة الإلكترونية باستخدامgcloud config set projectPROJ_IDأولاً. - نشر نموذج التطبيق باستخدام
gcloud app deploy - تأكَّد من أنّ التطبيق يعمل على النحو المتوقّع بدون أخطاء. إذا أكملت الدرس التطبيقي حول الترميز 20، سيعرض برنامج الحماية المتقدّمة معلومات تسجيل دخول المستخدم (البريد الإلكتروني للمستخدم، و"شارة المشرف" المحتملة، وزر تسجيل الدخول/الخروج) في أعلى الصفحة مع أحدث الزيارات (كما هو موضّح أدناه).

يؤدي تسجيل الدخول كمستخدم عادي إلى عرض عنوان البريد الإلكتروني للمستخدم، ويتغيّر الزر "تسجيل الدخول" إلى الزر "تسجيل الخروج"، كما يلي:

يؤدي تسجيل الدخول كمستخدم مشرف إلى عرض عنوان البريد الإلكتروني للمستخدم مع عبارة "(مشرف)" بجانبه:

4. تفعيل واجهات برمجة تطبيقات أو خدمات جديدة في Google Cloud
مقدّمة
يستخدم تطبيق الوحدة التدريبية 20 واجهتَي برمجة التطبيقات App Engine NDB وUsers، وهما خدمتان مجمّعتان لا تتطلّبان إعدادًا إضافيًا، ولكن خدمات Cloud المستقلة تتطلّب ذلك، وسيستخدم التطبيق المعدَّل كلاً من Cloud Identity Platform وCloud Datastore (من خلال مكتبة برامج Cloud NDB). بالإضافة إلى ذلك، يتطلّب تحديد مستخدمي مشرفي App Engine استخدام Cloud Resource Manager API.
التكلفة
- تتضمّن App Engine وCloud Datastore حصصًا ضمن "الطبقة المجانية دائمًا"، وطالما أنّك لا تتجاوز هذه الحدود، لن يتم تحصيل رسوم منك عند إكمال هذا البرنامج التعليمي. يمكنك أيضًا الاطّلاع على صفحة أسعار App Engine وصفحة أسعار Cloud Datastore للحصول على مزيد من التفاصيل.
- يتم تحصيل رسوم استخدام "منصة Cloud Identity" استنادًا إلى عدد المستخدمين النشطين شهريًا أو عمليات التحقّق من المصادقة، ويتوفّر إصدار "مجاني" لكل نموذج استخدام. يمكنك الاطّلاع على صفحة الأسعار لمزيد من التفاصيل. بالإضافة إلى ذلك، على الرغم من أنّ App Engine وCloud Datastore تتطلّبان الفوترة، لا يتطلّب استخدام GCIP في حد ذاته تفعيل الفوترة طالما أنّك لا تتجاوز حصصها اليومية غير المرتبطة بأي وسيلة دفع، لذا ضع ذلك في اعتبارك بالنسبة إلى مشاريع Cloud التي لا تتضمّن واجهات برمجة تطبيقات أو خدمات Cloud تتطلّب الفوترة.
- إنّ استخدام Cloud Resource Manager API مجاني في معظم الحالات وفقًا لصفحة الأسعار.
يُفعّل المستخدمون واجهات Cloud API من وحدة تحكّم Cloud أو من سطر الأوامر (عبر الأمر gcloud، وهو جزء من Cloud SDK)، وذلك حسب تفضيلاتك. لنبدأ بواجهتَي برمجة التطبيقات Cloud Datastore وCloud Resource Manager.
من Cloud Console
انتقِل إلى صفحة "المكتبة" في "إدارة واجهات برمجة التطبيقات" (للمشروع الصحيح) في Cloud Console، وابحث عن واجهة برمجة تطبيقات باستخدام شريط البحث. 
فعِّل واجهات برمجة التطبيقات التالية:
ابحث عن الزر تفعيل وانقر عليه لكل واجهة برمجة تطبيقات على حدة. قد يُطلب منك تقديم معلومات الفوترة. على سبيل المثال، إليك صفحة Resource Manager API:

يتغيّر الزر إلى "إدارة" بعد تفعيله (عادةً بعد بضع ثوانٍ):

فعِّل Cloud Datastore بالطريقة نفسها:

من سطر الأوامر
على الرغم من أنّ تفعيل واجهات برمجة التطبيقات من وحدة التحكّم يقدّم معلومات مرئية، يفضّل البعض استخدام سطر الأوامر. يمكنك أيضًا الاستفادة من ميزة إضافية وهي إمكانية تفعيل أي عدد من واجهات برمجة التطبيقات في الوقت نفسه. أدخِل هذا الأمر لتفعيل كلّ من Cloud Datastore API وCloud Resource Manager API وانتظِر إلى أن تكتمل العملية، كما هو موضّح هنا:
$ gcloud services enable cloudresourcemanager.googleapis.com datastore.googleapis.com Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.
قد يُطلب منك تقديم معلومات الفوترة.
يُطلق على "عناوين URL" لكل واجهة برمجة تطبيقات مستخدَمة في الأمر أعلاه أسماء خدمات واجهة برمجة التطبيقات، ويمكن العثور عليها في أسفل صفحة المكتبة لكل واجهة برمجة تطبيقات. إذا أردت تفعيل واجهات Cloud API الأخرى لتطبيقاتك، يمكنك العثور على أسماء الخدمات الخاصة بها في صفحات واجهات برمجة التطبيقات المعنية. يعرض هذا الأمر جميع أسماء الخدمات لواجهات برمجة التطبيقات التي يمكنك تفعيلها:
gcloud services list --available --filter="name:googleapis.com".
سواء في "وحدة تحكّم Google Cloud" أو في سطر الأوامر، بعد إكمال الخطوات المذكورة أعلاه، سيصبح بإمكان النموذج الوصول إلى واجهات برمجة التطبيقات هذه. تتمثّل الخطوات التالية في تفعيل "منصة Cloud Identity" وإجراء تغييرات الرموز البرمجية اللازمة.
تفعيل Cloud Identity Platform وإعداده (وحدة تحكّم Google Cloud فقط)
Cloud Identity Platform هي خدمة في Marketplace لأنّها تتصل بمورد خارج Google Cloud أو تعتمد عليه، مثل مصادقة Firebase. في الوقت الحالي، يمكنك تفعيل خدمات Marketplace من Cloud Console فقط. يُرجى اتّباع الخطوات التالية:
- انتقِل إلى صفحة "منصة Cloud Identity" في Cloud Marketplace وانقر على الزر تفعيل. يمكنك الترقية من مصادقة Firebase إذا طُلب منك ذلك، ما يتيح لك الاستفادة من ميزات إضافية، مثل الميزات الموضّحة سابقًا في قسم الخلفية. في ما يلي صفحة "السوق" التي توضّح الزر تفعيل:

- بعد تفعيل Identity Platform، قد يتم نقلك تلقائيًا إلى صفحة موفّرو الهوية. إذا لم يكن كذلك، استخدِم هذا الرابط السهل للانتقال إلى هناك.

- فعِّل موفّر Google Auth. في حال عدم إعداد أي موفّرين، انقر على إضافة موفّر واختَر Google. عند الرجوع إلى هذه الشاشة، من المفترض أن يكون الخيار Google مفعّلاً. Google هو مزوّد المصادقة الوحيد الذي نستخدمه في هذا البرنامج التعليمي لتكرار خدمة "المستخدمون" في App Engine كخدمة بسيطة لتسجيل الدخول باستخدام حساب Google. في تطبيقاتك، يمكنك تفعيل موفّري مصادقة إضافيين.
- بعد اختيار Google ومقدّمي خدمات المصادقة الآخرين المطلوبين وإعدادهم، انقر على تفاصيل إعداد التطبيق، ثم انسخ
apiKeyوauthDomainفي العنصرconfigضمن علامة التبويب "الويب" من نافذة مربّع الحوار التي تظهر، واحفظهما في مكان آمن. لماذا لا يتم نسخها كلها؟ إنّ المقتطف في هذا المربّع الحواري مبرمَج مسبقًا وقديم، لذا ما عليك سوى حفظ الأجزاء الأكثر أهمية واستخدامها في الرمز مع المزيد من الاستخدام المتزامن لخدمة Firebase Auth. بعد نسخ القيم وحفظها في مكان آمن، انقر على الزر إغلاق لإكمال جميع خطوات الإعداد اللازمة.
4. تعديل الإعدادات
تشمل التحديثات في الإعداد تغيير ملفات الإعداد المختلفة بالإضافة إلى إنشاء ما يعادل App Engine ولكن ضمن نظام Cloud Identity Platform المتكامل.
appengine_config.py
- في حال الترقية إلى Python 3، احذف
appengine_config.py - إذا كنت تخطّط للانتقال إلى Identity Platform ولكنك تريد البقاء على الإصدار 2 من Python، لا تحذف الملف. بدلاً من ذلك، سنعدّله لاحقًا أثناء نقل Python 2 إلى إصدار أحدث.
requirements.txt
لم يدرج ملف requirements.txt الخاص بالوحدة 20 سوى Flask. بالنسبة إلى الوحدة 21، أضِف الحِزم التالية:
يجب أن يظهر محتوى requirements.txt الآن على النحو التالي:
flask
google-auth
google-cloud-ndb
google-cloud-resource-manager
firebase-admin
app.yaml
- تتطلّب الترقية إلى الإصدار 3 من Python تبسيط ملف
app.yaml. أزِل كل شيء باستثناء توجيه وقت التشغيل، واضبطه على إصدار متوافق حاليًا من Python 3. يستخدم المثال حاليًا الإصدار 3.10. - إذا كنت ستواصل استخدام Python 2، لا تتّخذ أي إجراء هنا بعد.
قبل:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
لا يتضمّن نموذج تطبيق Module 20 معالجات ملفات ثابتة. إذا كانت تطبيقات الشركة تفعل ذلك، لا تغيّرها. يمكنك إزالة جميع معالِجات النصوص البرمجية إذا أردت ذلك أو تركها كمرجع طالما أنّك غيّرت مقابضها إلى auto، كما هو موضّح في دليل نقل البيانات app.yaml. بعد إجراء هذه التغييرات، تم تبسيط app.yaml المعدَّل للغة Python 3 ليصبح على النحو التالي:
بعد:
runtime: python310
تعديلات أخرى على الإعدادات
سواء كنت ستواصل استخدام Python 2 أو ستنتقل إلى Python 3، إذا كان لديك مجلد lib، احذفه.
5- تعديل الرمز البرمجي للتطبيق
يتضمّن هذا القسم تعديلات على ملف التطبيق الرئيسي، main.py، ما يؤدي إلى استبدال خدمة "مستخدمو App Engine" بمنصة Cloud Identity. بعد تعديل التطبيق الرئيسي، عليك تعديل نموذج الويب templates/index.html.
تعديل عمليات الاستيراد والإعداد
اتّبِع الخطوات التالية لتعديل عمليات الاستيراد وتهيئة موارد التطبيق:
- بالنسبة إلى عمليات الاستيراد، استبدِل App Engine NDB بـ Cloud NDB.
- بالإضافة إلى Cloud NDB، استورِد أيضًا Cloud Resource Manager.
- تستند Identity Platform إلى Firebase Auth، لذا عليك استيراد حزمة تطوير البرامج (SDK) الخاصة بمدير Firebase.
- تتطلّب Cloud APIs استخدام برنامج واجهة برمجة تطبيقات، لذا ابدأه في Cloud NDB أسفل عملية إعداد Flask مباشرةً.
على الرغم من استيراد حزمة Cloud Resource Manager هنا، سنستخدمها في مرحلة لاحقة عند تهيئة التطبيق. في ما يلي عمليات الاستيراد والتهيئة من الوحدة 20، بالإضافة إلى الشكل الذي يجب أن تبدو عليه الأقسام بعد تنفيذ التغييرات المذكورة أعلاه:
قبل:
from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb
app = Flask(__name__)
بعد:
from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app
# initialize Flask and Cloud NDB API client
app = Flask(__name__)
ds_client = ndb.Client()
الدعم لمستخدمي مشرفي App Engine
هناك عنصران يجب إضافتهما إلى التطبيق الذي يتيح التعرّف على المستخدمين المشرفين:
_get_gae_admins(): تجمع مجموعة من المستخدمين المشرفين، ويتم استدعاؤها مرة واحدة وحفظهاis_admin(): يتحقّق مما إذا كان المستخدم الذي سجّل الدخول هو مشرف، ويتم استدعاؤه عند تسجيل دخول أي مستخدم
تستدعي الدالة المساعدة، _get_gae_admins()، واجهة برمجة تطبيقات Resource Manager API لجلب سياسة السماح allow-policy الحالية في Cloud IAM. تحدّد سياسة السماح الأدوار التي يتم منحها الجهات الرئيسية (المستخدمون من البشر وحسابات الخدمة وما إلى ذلك) وتفرضها. تشمل عملية الإعداد ما يلي:
- استرداد رقم تعريف مشروع على السحابة الإلكترونية (
PROJ_ID) - إنشاء عميل لواجهة Resource Manager API (
rm_client) - إنشاء مجموعة (للقراءة فقط) من أدوار مشرف App Engine (
_TARGETS)
يتطلّب Resource Manager رقم تعريف مشروع على السحابة الإلكترونية، لذا استورِد google.auth.default() واستدعِ هذه الدالة للحصول على رقم تعريف المشروع. يتضمّن هذا الطلب مَعلمة تبدو كعنوان URL ولكنّها نطاق أذونات OAuth2. عند تشغيل تطبيقات في السحابة الإلكترونية، مثلاً على جهاز Compute Engine الظاهري أو تطبيق App Engine، يتم توفير حساب خدمة تلقائي يتمتّع بأذونات مميّزة واسعة النطاق. تماشيًا مع أفضل ممارسات مبدأ الامتياز الأقل، ننصحك بإنشاء حسابات خدمة يديرها المستخدم.
بالنسبة إلى طلبات البيانات من واجهة برمجة التطبيقات، من الأفضل تقليل نطاق تطبيقاتك إلى الحد الأدنى اللازم لتعمل بشكل صحيح. إنّ طلب بيانات من واجهة برمجة التطبيقات Resource Manager الذي سننفّذه هو get_iam_policy() الذي يحتاج إلى أحد النطاقات التالية ليعمل:
https://www.googleapis.com/auth/cloud-platformhttps://www.googleapis.com/auth/cloud-platform.read-onlyhttps://www.googleapis.com/auth/cloudplatformprojectshttps://www.googleapis.com/auth/cloudplatformprojects.readonly
يحتاج نموذج التطبيق إلى إذن بالقراءة فقط للوصول إلى سياسة السماح. لا تعدّل هذه الأداة السياسة ولا تحتاج إلى الوصول إلى المشروع بأكمله. وهذا يعني أنّ التطبيق لا يحتاج إلى أي من الأذونات الثلاثة الأولى المطلوبة. هذا هو كل ما هو مطلوب، وهو ما سننفّذه في نموذج التطبيق.
ينشئ النص الأساسي للدالة مجموعة فارغة من مستخدمي المشرف (admins)، ويسترد allow_policy من خلال get_iam_policy()، ويتكرر خلال جميع عمليات الربط بحثًا عن أدوار مشرف App Engine تحديدًا:
roles/viewerroles/editorroles/ownerroles/appengine.appAdmin
بالنسبة إلى كل دور مستهدَف تم العثور عليه، يتم تجميع المستخدمين الذين ينتمون إلى هذا الدور، وإضافتهم إلى المجموعة الإجمالية من المستخدمين المشرفين. وينتهي بعرض جميع المستخدمين المشرفين الذين تم العثور عليهم وتخزينهم مؤقتًا كثابت (_ADMINS) طوال مدة تشغيل مثيل App Engine هذا. سنتلقّى مكالمة قريبًا.
أضِف تعريف الدالة _get_gae_admins() التالي إلى main.py أسفل إنشاء مثيل لعميل Cloud NDB API مباشرةً (ds_client):
def _get_gae_admins():
'return set of App Engine admins'
# setup constants for calling Cloud Resource Manager API
_, PROJ_ID = default( # Application Default Credentials and project ID
['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()
_TARGETS = frozenset(( # App Engine admin roles
'roles/viewer',
'roles/editor',
'roles/owner',
'roles/appengine.appAdmin',
))
# collate users who are members of at least one GAE admin role (_TARGETS)
admins = set() # set of all App Engine admins
allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings: # bindings in IAM allow-policy
if b.role in _TARGETS: # only look at GAE admin roles
admins.update(user.split(':', 1).pop() for user in b.members)
return admins
عندما يسجّل المستخدمون الدخول إلى التطبيق، يحدث ما يلي:
- يتم إجراء فحص سريع من نموذج الويب بعد أن يسجّل المستخدم الدخول إلى Firebase.
- عندما تتغير حالة المصادقة في النموذج، يتم إجراء طلب
fetch()بنمط Ajax إلى/is_admin، ويكون معالج هذا الطلب هو الدالة التالية،is_admin(). - يتم تمرير رمز تعريف Firebase المميّز في نص POST إلى
is_admin()، الذي يستخرجه من العناوين ويطلب من "مدير SDK في Firebase" التحقّق من صحته. إذا كان المستخدم صالحًا، استخرِج عنوان بريده الإلكتروني وتحقَّق مما إذا كان مستخدمًا مشرفًا. - بعد ذلك، يتم عرض النتيجة المنطقية في النموذج كرمز حالة 200 ناجح.
أضِف is_admin() إلى main.py بعد _get_gae_admins() مباشرةً:
@app.route('/is_admin', methods=['POST'])
def is_admin():
'check if user (via their Firebase ID token) is GAE admin (POST) handler'
id_token = request.headers.get('Authorization')
email = auth.verify_id_token(id_token).get('email')
return {'admin': email in _ADMINS}, 200
يجب توفُّر جميع الرموز من كلتا الدالتين لتكرار الوظائف المتاحة من خدمة المستخدمين، وتحديدًا الدالة is_current_user_admin(). لقد نفّذ استدعاء الدالة هذا في الوحدة 20 كل العمل الشاق، على عكس الوحدة 21 التي نطبّق فيها حلاً بديلاً. والخبر السار هو أنّ التطبيق لم يعُد يعتمد على خدمة App Engine فقط، ما يعني أنّه يمكنك نقل تطبيقاتك إلى Cloud Run أو خدمات أخرى. علاوةً على ذلك، يمكنك أيضًا تغيير تعريف "مستخدم المشرف" لتطبيقاتك الخاصة من خلال التبديل إلى الأدوار المطلوبة في _TARGETS، بينما تكون خدمة المستخدمين مبرمَجة بشكل ثابت لأدوار مشرف App Engine.
إعداد "مصادقة Firebase" وتخزين المستخدمين المشرفين في App Engine مؤقتًا
كان بإمكاننا تهيئة Firebase Auth في الأعلى بالقرب من المكان نفسه الذي تم فيه تهيئة تطبيق Flask وإنشاء عميل Cloud NDB API، ولكن لم تكن هناك حاجة إلى ذلك إلى أن تم تحديد كل رمز المشرف، وهو ما وصلنا إليه الآن. وبالمثل، بعد تحديد _get_gae_admins()، يمكنك استدعاؤه لتخزين قائمة المستخدمين المشرفين مؤقتًا.
أضِف الأسطر التالية أسفل نص الدالة is_admin() مباشرةً:
# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()
تعديلات على نموذج بيانات الزيارات
لا يتغيّر نموذج بيانات Visit. يتطلّب الوصول إلى Datastore استخدامًا صريحًا لأداة إدارة سياق عميل Cloud NDB API، ds_client.context(). في الرمز البرمجي، يعني ذلك أنّه عليك تضمين طلبات Datastore في كل من store_visit() وfetch_visits() داخل كتل with في Python. هذا التحديث مطابق للوحدة 2. أجرِ التغييرات على النحو التالي:
قبل:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)
بعد:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
def fetch_visits(limit):
'get most recent visits'
with ds_client.context():
return Visit.query().order(-Visit.timestamp).fetch(limit)
نقل منطق تسجيل دخول المستخدم إلى نموذج الويب
تكون خدمة "المستخدمون" في App Engine من جهة الخادم، بينما تكون خدمة Firebase Auth وCloud Identity Platform من جهة العميل في الغالب. نتيجةً لذلك، يتم نقل جزء كبير من رمز إدارة المستخدمين في تطبيق "الوحدة 20" إلى نموذج الويب "الوحدة 21".
في main.py، يمرّر سياق الويب خمسة عناصر أساسية من البيانات إلى النموذج، وترتبط العناصر الأربعة الأولى المدرَجة بإدارة المستخدمين وتختلف حسب ما إذا كان المستخدم مسجّلاً الدخول أم لا:
who: عنوان البريد الإلكتروني للمستخدم إذا كان مسجّلاً الدخول أو user في الحالات الأخرىadmin: شارة (مشرف) إذا كان المستخدم الذي سجّل الدخول مشرفًاsign: لعرض الزر تسجيل الدخول أو تسجيل الخروجlink: روابط تسجيل الدخول أو الخروج عند النقر على الزرvisits: الزيارات الأحدث
قبل:
@app.route('/')
def root():
'main application (GET) handler'
store_visit(request.remote_addr, request.user_agent)
visits = fetch_visits(10)
# put together users context for web template
user = users.get_current_user()
context = { # logged in
'who': user.nickname(),
'admin': '(admin)' if users.is_current_user_admin() else '',
'sign': 'Logout',
'link': '/_ah/logout?continue=%s://%s/' % (
request.environ['wsgi.url_scheme'],
request.environ['HTTP_HOST'],
), # alternative to users.create_logout_url()
} if user else { # not logged in
'who': 'user',
'admin': '',
'sign': 'Login',
'link': users.create_login_url('/'),
}
# add visits to context and render template
context['visits'] = visits # display whether logged in or not
return render_template('index.html', **context)
سيتم نقل جميع عمليات إدارة المستخدمين إلى نموذج الويب، لذلك لن يتبقّى لدينا سوى الزيارات، ما سيؤدي إلى إعادة المعالج الرئيسي إلى ما كان عليه في تطبيق الوحدة 1:
بعد:
@app.route('/')
def root():
'main application (GET) handler'
store_visit(request.remote_addr, request.user_agent)
visits = fetch_visits(10)
return render_template('index.html', visits=visits)
تعديل نموذج الويب
كيف تبدو جميع التعديلات من القسم السابق في النموذج؟ نقلنا بشكل أساسي إدارة المستخدمين من التطبيق إلى خدمة Firebase Auth التي تعمل في النموذج، كما نقلنا جزءًا من كل الرموز البرمجية إلى JavaScript. لقد شهدنا انخفاضًا كبيرًا في main.py، لذا نتوقّع نموًا مماثلاً في templates/index.html.
قبل:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>
<p>
Welcome, {{ who }} <code>{{ admin }}</code>
<button id="logbtn">{{ sign }}</button>
</p><hr>
<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>
<script>
document.getElementById("logbtn").onclick = () => {
window.location.href = '{{ link }}';
};
</script>
</body>
</html>
استبدِل نموذج الويب بالكامل بالمحتوى أدناه:
بعد:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<script type="module">
// import Firebase module attributes
import {
initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
GoogleAuthProvider,
getAuth,
onAuthStateChanged,
signInWithPopup,
signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";
// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
};
// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});
// define login and logout button functions
function login() {
signInWithPopup(auth, provider);
};
function logout() {
signOut(auth);
};
// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
if (user && user != null) {
var email = user.email;
who.innerHTML = email;
logbtn.onclick = logout;
logbtn.innerHTML = "Logout";
var idToken = await user.getIdToken();
var rsp = await fetch("/is_admin", {
method: "POST",
headers: {Authorization: idToken}
});
var data = await rsp.json();
if (data.admin) {
admin.style.display = "inline";
}
} else {
who.innerHTML = "user";
admin.style.display = "none";
logbtn.onclick = login;
logbtn.innerHTML = "Login";
}
});
</script>
</head>
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>
<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>
<script>
var who = document.getElementById("who");
var admin = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>
يتضمّن نص HTML هذا العديد من المكوّنات، لذا سنستعرضها جزءًا جزءًا.
عمليات استيراد Firebase
بعد تجاوز عنوان الصفحة، استورِد مكوّنات Firebase المطلوبة، مع البقاء في عنوان مستند HTML. تم الآن تقسيم مكوّنات Firebase إلى وحدات متعدّدة لتحسين الكفاءة. يتم استيراد الرمز البرمجي لإعداد Firebase من وحدة تطبيق Firebase الرئيسية، بينما يتم استيراد جميع الدوال التي تدير مصادقة Firebase، وGoogle كموفّر مصادقة، وتسجيل الدخول والخروج، و "دالة رد الاتصال" لتغيير حالة المصادقة من وحدة Firebase Auth:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<script type="module">
// import Firebase module attributes
import {
initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
GoogleAuthProvider,
getAuth,
onAuthStateChanged,
signInWithPopup,
signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";
إعدادات Firebase
في وقت سابق من هذا البرنامج التعليمي، وخلال جزء إعداد Identity Platform، حفظت apiKey وauthDomain من مربّع الحوار تفاصيل إعداد التطبيق. أضِف هذه القيم إلى المتغيّر firebaseConfig في القسم التالي. يتوفّر رابط يؤدي إلى تعليمات أكثر تفصيلاً في التعليقات:
// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
};
إعداد Firebase
يتم في القسم التالي تهيئة Firebase باستخدام معلومات الإعداد هذه.
// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});
يضبط هذا الخيار إمكانية استخدام Google كموفّر مصادقة، ويوفر خيارًا معطّلاً لعرض أداة اختيار الحساب حتى إذا كان هناك حساب واحد فقط على Google مسجّلاً في جلسة المتصفح. بمعنى آخر، عندما يكون لديك حسابات متعددة، سيظهر لك "اختيار الحساب" هذا على النحو المتوقّع:
ومع ذلك، إذا كان هناك مستخدم واحد فقط في الجلسة، تكتمل عملية تسجيل الدخول تلقائيًا بدون أي تفاعل من المستخدم. (تظهر النافذة المنبثقة ثم تختفي). يمكنك فرض ظهور مربّع حوار اختيار الحساب لمستخدم واحد (بدلاً من تسجيل الدخول إلى التطبيق على الفور) من خلال إزالة التعليق من سطر المَعلمة المخصّصة. في حال تفعيل هذه الميزة، سيظهر منتقي الحسابات حتى عند تسجيل الدخول باستخدام حساب واحد: 
وظائف تسجيل الدخول والخروج
تشكّل أسطر الرمز التالية وظائف النقر على زر تسجيل الدخول أو الخروج:
// define login and logout button functions
function login() {
signInWithPopup(auth, provider);
};
function logout() {
signOut(auth);
};
إجراءات تسجيل الدخول والخروج
آخر قسم رئيسي في حزمة <script> هو الدالة التي يتم استدعاؤها عند كل تغيير في المصادقة (تسجيل الدخول أو تسجيل الخروج).
// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
if (user && user != null) {
var email = user.email;
who.innerHTML = email;
logbtn.onclick = logout;
logbtn.innerHTML = "Logout";
var idToken = await user.getIdToken();
var rsp = await fetch("/is_admin", {
method: "POST",
headers: {Authorization: idToken}
});
var data = await rsp.json();
if (data.admin) {
admin.style.display = "inline";
}
} else {
who.innerHTML = "user";
admin.style.display = "none";
logbtn.onclick = login;
logbtn.innerHTML = "Login";
}
});
</script>
</head>
يتم نقل الرمز البرمجي في الوحدة 20 الذي يحدّد ما إذا كان سيتم إرسال سياق نموذج "المستخدم مسجّل الدخول" أو سياق "المستخدم مسجّل الخروج" إلى هنا. تؤدي العبارة الشرطية في أعلى الصفحة إلى ظهور true إذا سجّل المستخدم الدخول بنجاح، ما يؤدي إلى تنفيذ الإجراءات التالية:
- يتم ضبط عنوان البريد الإلكتروني للمستخدم ليتم عرضه.
- يتغيّر زر تسجيل الدخول إلى تسجيل الخروج.
- يتم إجراء طلب على نمط Ajax إلى
/is_adminلتحديد ما إذا كان سيتم عرض شارة المستخدم المشرف(admin).
عندما يسجّل المستخدم الخروج، يتم تنفيذ عبارة else لإعادة ضبط جميع معلومات المستخدم:
- تم ضبط اسم المستخدم على user
- تمت إزالة أي شارة مشرف
- تمت إعادة تغيير زر تسجيل الخروج إلى تسجيل الدخول
متغيّرات النموذج
بعد انتهاء قسم العنوان، يبدأ النص الأساسي بمتغيرات النموذج التي يتم استبدالها بعناصر HTML التي تتغير حسب الحاجة:
- اسم المستخدم المعروض
- شارة المشرف
(admin)(إذا كان ذلك منطبقًا) - زر تسجيل الدخول أو تسجيل الخروج
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>
الزيارات الأخيرة ومتغيّرات عناصر HTML
لا يتغيّر رمز الزيارات الأحدث، وتضبط كتلة <script> النهائية المتغيرات لعناصر HTML التي تتغيّر عند تسجيل الدخول والخروج والمدرَجة أعلاه مباشرةً:
<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>
<script>
var who = document.getElementById("who");
var admin = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>
بهذا نكون قد انتهينا من التغييرات اللازمة في التطبيق وقالب الويب للتبديل من واجهتَي برمجة التطبيقات App Engine NDB وUsers إلى Cloud NDB وIdentity Platform، بالإضافة إلى الترقية إلى Python 3. تهانينا على وصولك إلى نموذج تطبيق الوحدة 21 الجديد. يمكنك مراجعة الإصدار الخاص بنا في مجلد مستودع الوحدة 21b.
الجزء التالي من الدرس التطبيقي حول الترميز اختياري (*) ومخصّص فقط للمستخدمين الذين يجب أن تظل تطبيقاتهم تعمل على الإصدار 2 من Python، وسيرشدك خلال الخطوات اللازمة للوصول إلى تطبيق يعمل على الوحدة 21 من الإصدار 2 من Python.
6. *إصدار متوافق مع Python 2
هذا القسم الاختياري مخصّص للمطوّرين الذين ينفّذون عملية نقل بيانات إلى Identity Platform ولكن عليهم مواصلة التشغيل على وقت تشغيل Python 2. إذا لم يكن هذا الأمر يهمّك، يمكنك تخطّي هذا القسم.
لإنشاء إصدار Python 2 يعمل من تطبيق Module 21، يجب توفُّر ما يلي:
- متطلبات وقت التشغيل: ملفات الإعداد المتوافقة مع Python 2 والتغييرات المطلوبة في التطبيق الرئيسي لتجنُّب حالات عدم التوافق مع Python 3
- تغيير بسيط في المكتبة: تم إيقاف الإصدار 2 من Python نهائيًا قبل إضافة بعض الميزات المطلوبة إلى مكتبة برامج Resource Manager. نتيجةً لذلك، تحتاج إلى طريقة بديلة للوصول إلى هذه الوظيفة غير المتوفّرة.
لنتّخذ هذه الخطوات الآن، بدءًا من الإعداد.
استعادة ملف appengine_config.py
في وقت سابق من هذا البرنامج التعليمي، تم توجيهك لحذف appengine_config.py لأنّه لا يتم استخدامه في وقت تشغيل Python 3 App Engine. بالنسبة إلى Python 2، يجب الحفاظ على الوحدة 20 appengine_config.py، كما يجب تعديلها لتتيح استخدام مكتبات الجهات الخارجية المضمّنة، أي grpcio وsetuptools. تكون هذه الحِزم مطلوبة عندما يستخدم تطبيق App Engine مكتبات برامج Cloud، مثل تلك الخاصة بـ Cloud NDB وCloud Resource Manager.
ستضيف هذه الحِزم إلى app.yaml مؤقتًا، ولكن لكي يتمكّن تطبيقك من الوصول إليها، يجب استدعاء الدالة pkg_resources.working_set.add_entry() من setuptools. يتيح ذلك للمكتبات التابعة لجهات خارجية والتي تم نسخها (المجمّعة ذاتيًا أو التي تم توفيرها) والمثبّتة في المجلد lib إمكانية التواصل مع المكتبات المضمّنة.
طبِّق التعديلات التالية على ملف appengine_config.py لإجراء هذه التغييرات:
قبل:
from google.appengine.ext import vendor
# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
لا يكفي هذا الرمز وحده لدعم استخدام setuptools وgrpcio. يجب إضافة بضعة أسطر أخرى، لذا عدِّل appengine_config.py ليصبح على النحو التالي:
بعد:
import pkg_resources
from google.appengine.ext import vendor
# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)
يمكنك الاطّلاع على مزيد من التفاصيل حول التغييرات المطلوبة لتوافق مكتبات برامج Cloud مع مستندات نقل الخدمات المجمّعة.
app.yaml
كما هو الحال مع appengine_config.py، يجب إعادة ملف app.yaml إلى إصدار متوافق مع Python 2. لِنبدأ بالوحدة الأصلية 20 app.yaml:
قبل:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
بالإضافة إلى setuptools وgrpcio المذكورتَين سابقًا، هناك اعتمادية (غير مرتبطة بشكل صريح بنقل البيانات إلى Identity Platform) تتطلّب استخدام مكتبة برامج Cloud Storage، وهذه المكتبة تحتاج إلى حزمة أخرى مضمّنة تابعة لجهة خارجية، وهي ssl. أضِف جميع الحزم الثلاث في قسم libraries جديد، واختَر أحدث الإصدارات المتاحة من هذه الحزم، إلى app.yaml:
بعد:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: grpcio
version: latest
- name: setuptools
version: latest
- name: ssl
version: latest
requirements.txt
بالنسبة إلى الوحدة التدريبية 21، أضفنا Google Auth وCloud NDB وCloud Resource Manager ومدير SDK في Firebase إلى الإصدار 3 من Python requirements.txt. تتسم حالة الإصدار 2 من Python بمزيد من التعقيد:
- توفّر واجهة Resource Manager API وظيفة allow-policy اللازمة لنموذج التطبيق، ولكن لم تكن هذه الوظيفة متاحة بعد في إصدار Python 2 النهائي من مكتبة عميل Cloud Resource Manager. (لا يتوفّر إلا في إصدار Python 3).
- نتيجةً لذلك، يجب توفير طريقة بديلة للوصول إلى هذه الميزة من واجهة برمجة التطبيقات. الحلّ هو استخدام مكتبة عميل Google APIs ذات المستوى الأدنى للتواصل مع واجهة برمجة التطبيقات. للتبديل إلى مكتبة برامج العميل هذه، استبدِل
google-cloud-resource-managerبحزمةgoogle-api-python-clientذات المستوى الأدنى. - بما أنّه تم إيقاف Python 2 نهائيًا، يتطلّب الرسم البياني للاعتماديات الذي يتيح استخدام الوحدة 21 قفل حِزم معيّنة على إصدارات محدّدة. يجب الإشارة إلى بعض الحِزم حتى إذا لم يتم تحديدها في
app.yamlPython 3.
قبل:
flask
بدءًا من الوحدة 20 requirements.txt، عدِّلها إلى ما يلي للحصول على تطبيق يعمل في الوحدة 21:
بعد:
grpcio==1.0.0
protobuf<3.18.0
six>=1.13.0
flask
google-gax<0.13.0
google-api-core==1.31.1
google-api-python-client<=1.11.0
google-auth<2.0dev
google-cloud-datastore==1.15.3
google-cloud-firestore==1.9.0
google-cloud-ndb
google-cloud-pubsub==1.7.0
firebase-admin
سيتم تعديل أرقام الحِزم والإصدارات في المستودع عند تغيير التبعيات، ولكن app.yaml يكفي لتشغيل التطبيق في وقت كتابة هذا المستند.
تعديلات أخرى على الإعدادات
إذا لم تكن قد حذفت المجلد lib من وقت سابق في هذا الدرس العملي، يمكنك إجراء ذلك الآن. باستخدام requirements.txt الذي تم تعديله حديثًا، نفِّذ الأمر المألوف التالي لتثبيت هذه المتطلبات في lib:
pip install -t lib -r requirements.txt # or pip2
إذا كان لديك الإصداران 2 و3 من Python مثبّتَين على نظام التطوير، قد تحتاج إلى استخدام pip2 بدلاً من pip.
تعديل الرمز البرمجي للتطبيق
لحسن الحظ، تتوفر معظم التغييرات المطلوبة في ملفات الإعداد. التغيير الوحيد المطلوب في الرمز البرمجي للتطبيق هو تعديل بسيط لاستخدام مكتبة عميل Google API ذات المستوى الأدنى بدلاً من مكتبة عميل Resource Manager للوصول إلى واجهة برمجة التطبيقات. لا يلزم إجراء أي تعديلات على نموذج الويب templates/index.html.
تعديل عمليات الاستيراد والإعداد
استبدِل مكتبة برامج Resource Manager (google.cloud.resourcemanager) بمكتبة برامج Google APIs (googleapiclient.discovery)، كما هو موضّح أدناه:
قبل:
from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app
بعد:
from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb
from googleapiclient import discovery
from firebase_admin import auth, initialize_app
الدعم لمستخدمي مشرفي App Engine
يجب إجراء بعض التغييرات في _get_gae_admins() لإتاحة استخدام مكتبة برامج العميل ذات المستوى الأدنى. لنناقش أولاً التغييرات التي ستطرأ، ثم سنقدّم لك كل الرموز اللازمة لإجراء التحديث.
يتطلّب رمز Python 2 استخدام كلّ من بيانات الاعتماد ورقم تعريف المشروع الذي يتم عرضه من google.auth.default(). لا يتم استخدام بيانات الاعتماد في Python 3، لذا تم تعيينها إلى متغيّر وهمي عام بشرطة سفلية ( _ ). بما أنّها مطلوبة لإصدار Python 2، غيِّر الشرطة السفلية إلى CREDS. بالإضافة إلى ذلك، بدلاً من إنشاء عميل لواجهة برمجة التطبيقات Resource Manager API، ستنشئ نقطة نهاية لخدمة واجهة برمجة التطبيقات، وهي تشبه في مفهومها عميل واجهة برمجة التطبيقات، لذا سنحتفظ باسم المتغيّر نفسه (rm_client). أحد الاختلافات هو أنّ إنشاء نقطة نهاية لخدمة يتطلّب توفير بيانات الاعتماد (CREDS).
تظهر هذه التغييرات في الرمز البرمجي أدناه:
قبل:
_, PROJ_ID = default( # Application Default Credentials and project ID
['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()
بعد:
CREDS, PROJ_ID = default( # Application Default Credentials and project ID
['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
الاختلاف الآخر هو أنّ مكتبة برامج Resource Manager تعرض عناصر allow-policy التي تستخدم ترميز السمات المنقّطة، بينما تعرض مكتبة البرامج ذات المستوى الأدنى قواميس Python يتم فيها استخدام الأقواس المربّعة ( [ ] ). على سبيل المثال، استخدِم binding.role لمكتبة برامج Resource Manager وbinding['role'] لمكتبة البرامج ذات المستوى الأدنى. يستخدم الإصدار السابق أيضًا أسماء "مفصولة بشرطة سفلية" بدلاً من المكتبة ذات المستوى الأدنى التي تفضّل الأسماء "بأسلوب CamelCase" بالإضافة إلى طريقة مختلفة قليلاً لتمرير مَعلمات واجهة برمجة التطبيقات.
في ما يلي الاختلافات في الاستخدام:
قبل:
allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings: # bindings in IAM allow-policy
if b.role in _TARGETS: # only look at GAE admin roles
admins.update(user.split(':', 1).pop() for user in b.members)
بعد:
allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']: # bindings in IAM allow-policy
if b['role'] in _TARGETS: # only look at GAE admin roles
admins.update(user.split(':', 1).pop() for user in b['members'])
بعد جمع كل هذه التغييرات معًا، استبدِل الرمز _get_gae_admins() في Python 3 بإصدار Python 2 المكافئ التالي:
def _get_gae_admins():
'return set of App Engine admins'
# setup constants for calling Cloud Resource Manager API
CREDS, PROJ_ID = default( # Application Default Credentials and project ID
['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
_TARGETS = frozenset(( # App Engine admin roles
'roles/viewer',
'roles/editor',
'roles/owner',
'roles/appengine.appAdmin',
))
# collate users who are members of at least one GAE admin role (_TARGETS)
admins = set() # set of all App Engine admins
allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']: # bindings in IAM allow-policy
if b['role'] in _TARGETS: # only look at GAE admin roles
admins.update(user.split(':', 1).pop() for user in b['members'])
return admins
لا تتطلّب وظيفة is_admin() أي تحديثات لأنّها تعتمد على _get_gae_admins() الذي تم تحديثه من قبل.
بهذا نكون قد انتهينا من التغييرات المطلوبة لنقل تطبيق Python 3 Module 21 إلى الإصدار 2 من Python. تهانينا على الوصول إلى نموذج تطبيق الوحدة 21 المعدَّل. يمكنك العثور على جميع الرموز في مجلد مستودع الوحدة التدريبية 21a.
7. الملخّص/التنظيف
تتمثّل الخطوات الأخيرة في هذا الدرس التطبيقي حول الترميز في التأكّد من أنّ الكيانات التي يمكن المصادقة عليها (المستخدمين أو حسابات الخدمة) التي تشغّل هذا التطبيق لديها الأذونات المناسبة للقيام بذلك، ثم تفعيل تطبيقك للتأكّد من أنّه يعمل على النحو المنشود وأنّ التغييرات تظهر في الناتج.
إمكانية قراءة سياسة السماح في "إدارة الهوية وإمكانية الوصول"
في السابق، عرّفناك على الأدوار الأربعة المطلوبة ليتم التعرّف عليك كمستخدم مشرف في App Engine، ولكن هناك الآن دور خامس يجب التعرّف عليه:
roles/viewerroles/editorroles/ownerroles/appengine.appAdminroles/resourcemanager.projectIamAdmin(للمدراء الذين يمكنهم الوصول إلى سياسة السماح في "إدارة الهوية وإمكانية الوصول")
يتيح الدور roles/resourcemanager.projectIamAdmin للمديرين تحديد ما إذا كان المستخدم النهائي عضوًا في أي من أدوار مشرفي App Engine. بدون عضوية في roles/resourcemanager.projectIamAdmin، ستتعذّر الطلبات المُرسَلة إلى Cloud Resource Manager API للحصول على سياسة السماح.
لا تحتاج إلى اتّخاذ أي إجراء صريح هنا لأنّ تطبيقك سيتم تشغيله ضمن حساب الخدمة التلقائي في App Engine الذي يتم منحه تلقائيًا عضوية في هذا الدور. حتى إذا كنت تستخدم حساب الخدمة التلقائي خلال مرحلة التطوير، ننصحك بشدة بإنشاء حساب خدمة يديره المستخدم واستخدامه مع الحدّ الأدنى من الأذونات المطلوبة ليعمل تطبيقك بشكلٍ سليم. لمنح عضوية لحساب خدمة من هذا النوع، شغِّل الأمر التالي:
$ gcloud projects add-iam-policy-binding PROJ_ID --member="serviceAccount:USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com" --role=roles/resourcemanager.projectIamAdmin
PROJ_ID هو رقم تعريف مشروع على السحابة الإلكترونية، وUSR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com هو حساب الخدمة المُدار من قِبل المستخدم الذي تنشئه لتطبيقك. يعرض هذا الأمر سياسة إدارة الهوية وإمكانية الوصول المعدَّلة لمشروعك حيث يمكنك التأكّد من أنّ حساب الخدمة لديه عضوية في roles/resourcemanager.projectIamAdmin. لمزيد من المعلومات، اطّلِع على المستندات المرجعية. أكرّر أنّه ليس عليك تنفيذ هذا الأمر في هذا الدرس التطبيقي حول الترميز، ولكن احفظه كمرجع لتحديث تطبيقاتك.
نشر التطبيق والتحقّق منه
حمِّل تطبيقك إلى السحابة الإلكترونية باستخدام الأمر gcloud app deploy العادي. بعد النشر، من المفترض أن تظهر لك وظائف مطابقة تقريبًا لتلك المتوفّرة في تطبيق الوحدة 20، باستثناء أنّك استبدلت خدمة "مستخدمو App Engine" بمنصّة Cloud Identity (وFirebase Auth) لإدارة المستخدمين:

أحد الاختلافات التي ستلاحظها مقارنةً بالوحدة التدريبية 20 هو أنّ النقر على "تسجيل الدخول" يؤدي إلى ظهور نافذة منبثقة بدلاً من إعادة التوجيه، كما هو موضح في بعض لقطات الشاشة أدناه. ومثل الوحدة 20، يختلف السلوك قليلاً حسب عدد حسابات Google التي تم تسجيلها في المتصفّح.
إذا لم يكن هناك أي مستخدمين مسجّلين في المتصفّح أو كان هناك مستخدم واحد لم يسجّل الدخول بعد، ستظهر نافذة منبثقة عامة لتسجيل الدخول باستخدام حساب Google:

إذا كان مستخدم واحد مسجّلاً في متصفّحك ولكنّه يسجّل الدخول في مكان آخر، لن يظهر أي مربّع حوار (أو سيظهر ويُغلق على الفور)، وسينتقل التطبيق إلى حالة تسجيل الدخول (يعرض عنوان البريد الإلكتروني للمستخدم وزر تسجيل الخروج).
قد يريد بعض المطوّرين توفير أداة اختيار حساب، حتى لمستخدم واحد:

لتنفيذ ذلك، أزِل التعليق من السطر provider.setCustomParameters({prompt: 'select_account'}); في نموذج الويب كما هو موضّح سابقًا.
إذا كان هناك عدة مستخدمين، سيظهر مربّع حوار لاختيار الحساب (انظر أدناه). إذا لم يكن المستخدم مسجّلاً الدخول بعد، سيُطلب منه ذلك. إذا كنت مسجّلاً الدخول، ستختفي النافذة المنبثقة، وسينتقل التطبيق إلى حالة تسجيل الدخول.

تبدو حالة تسجيل الدخول في الوحدة 21 مماثلة لواجهة المستخدم في الوحدة 20:

وينطبق الأمر نفسه عندما يسجّل مستخدم إداري الدخول:

على عكس الوحدة 21، تصل الوحدة 20 دائمًا إلى منطق محتوى نموذج الويب من التطبيق (الرمز البرمجي من جهة الخادم). من عيوب الوحدة 20 أنّه يتم تسجيل زيارة واحدة عندما يصل المستخدم النهائي إلى التطبيق لأول مرة، ويتم تسجيل زيارة أخرى عندما يسجّل المستخدم الدخول.
بالنسبة إلى الوحدة 21، تتم عملية تسجيل الدخول في نموذج الويب فقط (الرمز البرمجي من جهة العميل). لا يلزم إجراء رحلة من جهة الخادم لتحديد المحتوى الذي سيتم عرضه. المكالمة الوحيدة التي يتم إجراؤها إلى الخادم هي التحقّق من المستخدمين المشرفين بعد تسجيل دخول المستخدم النهائي. وهذا يعني أنّ عمليات تسجيل الدخول والخروج لا تسجّل زيارات إضافية، لذا تبقى قائمة الزيارات الأخيرة ثابتة لإجراءات إدارة المستخدمين. لاحظ أنّ لقطات الشاشة أعلاه تعرض المجموعة نفسها من أربع زيارات على مستوى عمليات تسجيل دخول متعدّدة للمستخدمين.
توضّح لقطات الشاشة الخاصة بالوحدة التدريبية 20 "خطأ الزيارة المزدوجة" في بداية هذا الدرس العملي. يتم عرض سجلّات الزيارات المنفصلة لكل إجراء تسجيل دخول أو خروج. تحقَّق من الطوابع الزمنية لأحدث زيارة لكل لقطة شاشة تعرض الترتيب الزمني.
تَنظيم
للجمهور العام
إذا انتهيت من استخدام التطبيق في الوقت الحالي، ننصحك بإيقاف تطبيق App Engine لتجنُّب تحمّل رسوم. ومع ذلك، إذا أردت إجراء المزيد من الاختبارات أو التجارب، تتضمّن منصة App Engine حصّة مجانية، وبالتالي لن يتم تحصيل رسوم منك طالما أنّك لا تتجاوز مستوى الاستخدام هذا. هذا السعر خاص بالحوسبة، ولكن قد تكون هناك رسوم أيضًا مقابل خدمات App Engine ذات الصلة، لذا يُرجى الاطّلاع على صفحة الأسعار للحصول على مزيد من المعلومات. إذا كان نقل البيانات هذا يتضمّن خدمات سحابية أخرى، يتم إصدار فواتير لها بشكل منفصل. في كلتا الحالتين، إذا كان ذلك منطبقًا، راجِع القسم "خاص بهذا الدرس التطبيقي حول الترميز" أدناه.
للتوضيح، يؤدي النشر على منصة حوسبة بدون خادم في Google Cloud، مثل App Engine، إلى تكبُّد تكاليف بسيطة للإنشاء والتخزين. تتضمّن Cloud Build حصة مجانية خاصة بها، وكذلك Cloud Storage. ويؤدي تخزين هذه الصورة إلى استهلاك جزء من هذه الحصة. ومع ذلك، قد تكون مقيمًا في منطقة لا تتوفّر فيها هذه الطبقة المجانية، لذا عليك الانتباه إلى استخدامك لمساحة التخزين لتقليل التكاليف المحتملة. تتضمّن "المجلدات" المحدّدة في Cloud Storage التي يجب مراجعتها ما يلي:
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- تعتمد روابط مساحة التخزين أعلاه على
PROJECT_IDوLOCتك، على سبيل المثال، "us" إذا كان تطبيقك مستضافًا في الولايات المتحدة.
من ناحية أخرى، إذا كنت لن تواصل استخدام هذا التطبيق أو غيره من الدروس التعليمية البرمجية المتعلقة بنقل البيانات وأردت حذف كل شيء تمامًا، عليك إيقاف مشروعك.
خاص بهذا الدرس التطبيقي حول الترميز
الخدمات المدرَجة أدناه خاصة بهذا الدرس التطبيقي حول الترميز. يُرجى الرجوع إلى مستندات كل منتج للحصول على مزيد من المعلومات:
- يتم توفير خدمة App Engine Datastore من خلال Cloud Datastore (Cloud Firestore في وضع Datastore) الذي يتضمّن أيضًا طبقة مجانية. يمكنك الاطّلاع على صفحة الأسعار للحصول على مزيد من المعلومات.
- يتضمّن استخدام "منصة Cloud Identity" مستوى معيّنًا من "الاستخدام المجاني"، وذلك حسب الخدمات التي تستخدمها. يمكنك الاطّلاع على صفحة الأسعار لمزيد من التفاصيل.
- إنّ استخدام Cloud Resource Manager API مجاني في معظم الحالات وفقًا لصفحة الأسعار.
الخطوات التالية
بالإضافة إلى هذا البرنامج التعليمي، تتضمّن وحدات نقل البيانات الأخرى التي تركّز على التوقّف عن استخدام الخدمات المجمّعة القديمة ما يلي:
- الوحدة 2: نقل البيانات من App Engine
ndbإلى Cloud NDB - الوحدات من 7 إلى 9: نقل البيانات من App Engine Task Queue (مهام الدفع) إلى Cloud Tasks
- الوحدتان 12 و13: نقل البيانات من App Engine Memcache إلى Cloud Memorystore
- الوحدتان 15 و16: نقل البيانات من App Engine Blobstore إلى Cloud Storage
- الوحدتان 18 و19: نقل البيانات من "قائمة انتظار المهام" في App Engine (مهام السحب) إلى Cloud Pub/Sub
لم تعُد App Engine هي المنصة الوحيدة بدون خادم في Google Cloud. إذا كان لديك تطبيق صغير على App Engine أو تطبيق بوظائف محدودة وتريد تحويله إلى خدمة مصغّرة مستقلة، أو إذا كنت تريد تقسيم تطبيق متكامل إلى عدة مكونات قابلة لإعادة الاستخدام، ستكون هذه أسبابًا وجيهة للنقل إلى Cloud Functions. إذا أصبحت عملية إنشاء الحاويات جزءًا من سير عمل تطوير التطبيقات، خاصةً إذا كانت تتألف من مسار CI/CD (التكامل المستمر/التسليم المتواصل أو النشر المستمر)، ننصحك بالانتقال إلى Cloud Run. تتناول الوحدات التدريبية التالية هذه السيناريوهات:
- نقل البيانات من App Engine إلى Cloud Functions: يمكنك الاطّلاع على الوحدة 11
- الانتقال من App Engine إلى Cloud Run: يمكنك الاطّلاع على الوحدة 4 لتضمين تطبيقك في حاوية باستخدام Docker، أو الوحدة 5 لإجراء ذلك بدون حاويات أو معرفة بـ Docker أو
Dockerfile
إنّ الانتقال إلى منصة أخرى بلا خادم هو أمر اختياري، وننصحك بمراجعة أفضل الخيارات لتطبيقاتك وحالات الاستخدام قبل إجراء أي تغييرات.
بغض النظر عن وحدة النقل التي ستختارها، يمكنك الوصول إلى كل محتوى Serverless Migration Station (الدروس البرمجية والفيديوهات ورمز المصدر [عند توفّره]) من خلال مستودع المصدر المفتوح. يوفّر مستودع README أيضًا إرشادات حول عمليات نقل البيانات التي يجب أخذها في الاعتبار وأي "ترتيب" ذي صلة لوحدات نقل البيانات.
8. مراجع إضافية
في ما يلي مراجع إضافية للمطوّرين الذين يريدون استكشاف هذه الوحدات أو وحدات نقل البيانات ذات الصلة بشكل أكبر. في ما يلي، يمكنك تقديم ملاحظاتك حول هذا المحتوى والعثور على روابط للرمز البرمجي ومختلف المستندات التي قد تجدها مفيدة.
مشاكل/ملاحظات بشأن الدروس التطبيقية حول الترميز
إذا واجهت أي مشاكل في هذا الدرس العملي، يُرجى البحث عن مشكلتك أولاً قبل إرسالها. روابط للبحث عن مشاكل جديدة وإنشائها:
مراجع لنقل البيانات
يمكنك العثور على روابط لمجلدات المستودع الخاصة بالوحدة التدريبية 20 (البداية) والوحدة التدريبية 21 (النهاية) في الجدول أدناه.
Codelab | Python 2 | Python 3 |
(غير متوفر) | ||
الوحدة 21 (هذا الدرس التطبيقي حول الترميز) |
المراجع على الإنترنت
في ما يلي مراجع ذات صلة بهذا البرنامج التعليمي:
منصة Cloud Identity وCloud Marketplace
- صفحة منتج Identity Platform
- مصادقة Firebase
- صفحة مقارنة منتجات Identity Platform وFirebase Auth
- معلومات أسعار Identity Platform
- حصص Identity Platform (والاستخدام بدون أدوات)
- إعداد موفّري Identity Platform
- صفحة منتج Cloud Marketplace
- صفحة Identity Platform في Marketplace
Cloud Resource Manager وCloud IAM ومدير SDK في Firebase
- صفحة منتج "إدارة الموارد"
- معلومات أسعار Resource Manager
- مكتبة برامج Resource Manager
- نظرة عامة على Cloud IAM (الأدوار وسياسة السماح وما إلى ذلك)
- مدير SDK في Firebase (بايثون)
App Engine Users وApp Engine NDB وCloud NDB وCloud Datastore
- نظرة عامة على مستخدمي App Engine
- مستندات NDB في App Engine
- مستودع App Engine NDB
- مكتبة برامج Cloud NDB
- مستودع Cloud NDB
- صفحة منتج Cloud Datastore
- معلومات أسعار Cloud Datastore
مراجع "وحدة نقل البيانات" الأخرى
- مقدمة إلى "وحدة نقل البيانات"
- جميع موارد "محطة نقل البيانات بدون خادم"
- مستندات نقل البيانات إلى Python 3
- الدرس التطبيقي حول الترميز 17 من وحدة نقل البيانات "استخدام الخدمات المجمّعة في أوقات التشغيل من الجيل الثاني"
- الدرس التطبيقي حول الترميز في الوحدة 20 من عملية نقل البيانات "إضافة خدمة المستخدمين في App Engine إلى تطبيقات Flask"
نقل بيانات App Engine
- استخدام مكتبات تابعة لجهات خارجية في تطبيقات Python 2
- التغييرات على
app.yamlفي أوقات التشغيل من الجيل الثاني (Python 3) - دليل نقل البيانات إلى Cloud NDB
- محتوى نقل البيانات في Cloud NDB
النظام الأساسي لـ App Engine
- مستندات App Engine
- وقت تشغيل Python 2 App Engine (البيئة العادية)
- استخدام المكتبات المضمّنة في App Engine على Python 2 App Engine
- وقت تشغيل Python 3 App Engine (البيئة العادية)
- الاختلافات بين أوقات تشغيل Python 2 و3 في App Engine (البيئة العادية)
- دليل نقل البيانات من Python 2 إلى Python 3 في App Engine (البيئة العادية)
- معلومات الأسعار والحصص في App Engine
- إطلاق الجيل الثاني من منصة App Engine (2018)
- مقارنة بين الجيل الأول والثاني من المنصات
- الدعم الطويل الأمد لأوقات التشغيل القديمة
- أمثلة على نقل المستندات
- عينات نقل البيانات التي يقدّمها المنتدى
SDK للسحاب
- Google Cloud SDK
- أداة سطر الأوامر
gcloudفي Cloud SDK - تفعيل واجهات Google API (وإيقافها)
- أداة إدارة واجهات برمجة التطبيقات في "وحدة تحكّم Google Cloud" (تفعيل/إيقاف واجهات برمجة التطبيقات)
- تفعيل واجهات Google API باستخدام
gcloud - إدراج واجهات Google API باستخدام
gcloud
معلومات أخرى حول السحابة الإلكترونية
- Python على Google Cloud
- مستندات مكتبات برامج Python
- مستودعات مكتبات برامج Python
- فئة"دائمًا مجانية"
- Cloud SDK
- أداة سطر الأوامر
gcloudفي Cloud SDK - جميع مستندات Google Cloud
الفيديوهات
- Serverless Migration Station
- أداة "استكشافات" بدون خادم
- الاشتراك في قناة Google Cloud Tech
- الاشتراك في Google Developers
الترخيص
يخضع هذا العمل لترخيص المشاع الإبداعي مع نسب العمل إلى مؤلفه 2.0 Generic License.