استخدام حزمة SDK للمشرف مع SQL Connect

Firebase Admin SDK هي مجموعة من مكتبات الخادم التي تتيح لك التفاعل مع Firebase من بيئات مميزة لتنفيذ إجراءات، مثل إجراء طلبات بحث وعمليات تغيير في خدمة Firebase SQL Connect لإدارة البيانات المجمّعة وعمليات أخرى بأذونات مميزة وعالية المستوى وبيانات اعتماد منتحَلة.

توفر لك Admin SDK واجهة برمجة تطبيقات لاستدعاء العمليات في وضعَي القراءة/الكتابة والقراءة فقط. باستخدام عمليات القراءة فقط، يمكنك تنفيذ وظائف إدارية لا يمكنها تعديل البيانات في قواعد البيانات، ما يمنحك راحة البال.

إعداد حزمة Admin SDK

لبدء استخدام حزمة Admin SDK مع Firebase SQL Connect على الخادم، عليك أولاً تثبيت حزمة Admin SDK لـ Admin SDKNode.js وإعدادها.

إعداد حزمة Admin SDK في النصوص البرمجية

لإعداد حزمة SDK، استورِد إضافات SQL Connect و أعلِن عن رقم تعريف خدمة مشروعك وموقعه الجغرافي.


import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';

// If you'd like to use OAuth2 flows and other credentials to log in,
// visit https://firebase.google.com/docs/admin/setup#initialize-sdk
// for alternative ways to initialize the SDK.

const app = initializeApp();

const dataConnect = getDataConnect({
    serviceId: 'serviceId',
    location: 'us-west2'
});

تصميم طلبات البحث وعمليات التغيير لاستخدامها مع Admin SDK

تكون Admin SDK مفيدة لتشغيل عمليات SQL Connect، مع مراعاة الاعتبارات التالية.

فهم حزمة SDK وتوجيه العملية @auth(level: NO_ACCESS)

بما أنّ Admin SDK تعمل بامتيازات، يمكنها تنفيذ أيّ من طلبات البحث وعمليات التغيير بغض النظر عن مستويات الوصول التي تم ضبطها باستخدام @auth توجيهات، بما في ذلك مستوى NO_ACCESS.

إذا كنت تنظّم طلبات البحث وعمليات التغيير الإدارية في ملفات مصدر .gql لاستيرادها إلى النصوص البرمجية الإدارية، ننصحك بوضع علامة على العمليات الإدارية بدون أي مستوى وصول إلى التفويض، أو ربما أن تكون أكثر وضوحًا وتضبطها على NO_ACCESS. في كلتا الحالتَين، يمنع ذلك تنفيذ هذه العمليات من العملاء أو في سياقات أخرى غير مميزة.

استخدام حزمة SDK مع المحاكي SQL Connect

في بيئات النماذج الأولية والاختبار، قد يكون من المفيد إجراء عمليات إعداد البيانات وعمليات أخرى على البيانات المحلية. تتيح لك Admin SDK تبسيط مهام سير العمل لأنّها يمكنها تجاهل المصادقة والتفويض للتدفقات المحلية. (يمكنك أيضًا الموافقة صراحةً على الامتثال لإعدادات المصادقة والتفويض لعملياتك من خلال تسجيل الدخول باسم مستخدم آخر.)

تتصل حزم Firebase Admin SDK تلقائيًا بالمحاكي SQL Connect عند ضبط متغيّر البيئة DATA_CONNECT_EMULATOR_HOST:

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

يمكنك الاطّلاع على ما يلي للحصول على مزيد من المعلومات:

تنفيذ العمليات الإدارية

يتم توفير Admin SDK للعمليات المميزة على بياناتك المهمة.

توفّر حزمة Admin SDK ثلاث مجموعات من واجهات برمجة التطبيقات:

  • حزم Admin SDK التي تم إنشاؤها، وهي حزم SDK آمنة من ناحية النوع تم إنشاؤها من تعريفات gql بالطريقة نفسها التي تنشئ بها حزم SDK للعميل.
  • واجهة عامة لتشغيل عمليات GraphQL عشوائية، حيث ينفّذ الرمز البرمجي طلبات البحث وعمليات التغيير ويمررها إلى طريقة القراءة والكتابة executeGraphql أو طريقة القراءة فقط executeGraphqlRead.
  • واجهة متخصصة لعمليات البيانات المجمّعة، والتي بدلاً من طرق executeGraphql العامة، تعرض طرقًا مخصصة لعمليات التغيير: insert وinsertMany وupsert وupsertMany.

إدارة البيانات باستخدام حزم SDK التي تم إنشاؤها

يمكنك إنشاء حزم Admin SDK من تعريفات gqlبالطريقة نفسها التي تنشئ بها حزم SDK للعميل.

تحتوي حزمة Admin SDK التي تم إنشاؤها على واجهات ودوال تتطابق مع تعريفات gql، والتي يمكنك استخدامها لإجراء عمليات على قاعدة البيانات. على سبيل المثال، لنفترض أنّك أنشأت حزمة SDK لقاعدة بيانات الأغاني، بالإضافة إلى طلب بحث getSongs:

import { initializeApp } from "firebase-admin/app";
import { getSongs } from "@dataconnect/admin-generated";

const adminApp = initializeApp();

const songs = await getSongs(
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

أو لتحديد إعدادات الموصّل:

import { initializeApp } from "firebase-admin/app";
import { getDataConnect } from "firebase-admin/data-connect";
import {
  connectorConfig,
  getSongs,
} from "@dataconnect/admin-generated";

const adminApp = initializeApp();
const adminDc = getDataConnect(connectorConfig);

const songs = await getSongs(
  adminDc,
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

انتحال هوية مستخدم غير مصادَق عليه

تم تصميم حزم Admin SDK ليتم تشغيلها من بيئات موثوق بها، وبالتالي يمكنها الوصول إلى قواعد البيانات بدون قيود.

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

في المثال أعلاه، يتم تنفيذ طلب البحث getSongs كمستخدم غير مصادَق عليه.

انتحال هوية مستخدم

يمكنك أيضًا إجراء عمليات نيابةً عن مستخدمين معيّنين من خلال تمرير جزء من رمز Firebase Authenticationأو كلّه في الخيار impersonate، ويجب على الأقل تحديد رقم تعريف المستخدم في طلب `sub`. (هذه هي القيمة نفسها لقيمة الخادم auth.uid التي يمكنك الرجوع إليها في عمليات GraphQL SQL Connect.)

عند انتحال هوية مستخدم، لن تنجح العملية إلا إذا اجتازت بيانات المستخدم التي قدّمتها عمليات التحقّق من المصادقة المحدّدة في تعريف GraphQL.

إذا كنت تستدعي حزمة SDK التي تم إنشاؤها من نقطة نهاية يمكن الوصول إليها علنًا، من المهم أن تتطلّب نقطة النهاية المصادقة وأن تتحقّق من سلامة رمز المصادقة قبل استخدامه لانتحال هوية مستخدم.

عند استخدام Cloud Functions القابلة للاستدعاء، يتم التحقّق تلقائيًا من رمز المصادقة ويمكنك استخدامه كما في المثال التالي:

import { HttpsError, onCall } from "firebase-functions/https";

export const callableExample = onCall(async (req) => {
    const authClaims = req.auth?.token;
    if (!authClaims) {
        throw new HttpsError("unauthenticated", "Unauthorized");
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

بخلاف ذلك، استخدِم Admin SDK verifyIdToken للتحقّق من رمز المصادقة وفك ترميزه. على سبيل المثال، لنفترض أنّ نقطة النهاية تم تنفيذها كدالة HTTP عادية وأنّك مرّرت رمز Firebase Authentication إلى نقطة النهاية باستخدام عنوان authorization، كما هو معتاد:

import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";

const auth = getAuth();

export const httpExample = onRequest(async (req, res) => {
    const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
    if (!token) {
        res.sendStatus(401);
        return;
    }
    let authClaims;
    try {
        authClaims = await auth.verifyIdToken(token);
    } catch {
        res.sendStatus(401);
        return;
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

عند إجراء مهام إدارية حقيقية فقط، مثل نقل البيانات، من بيئة آمنة لا يمكن الوصول إليها علنًا، عليك تحديد رقم تعريف مستخدم لم يتم إنشاؤه من مصدر يمكن التحقّق منه:

// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
  undefined,
  { impersonate: { authClaims } }
);

التشغيل بإذن وصول غير مُقيَّد

إذا كنت تجري عملية تتطلب أذونات على مستوى المشرف، احذف المَعلمة `impersonate` من الاستدعاء:

await upsertSong(adminDc, {
  title: songTitle_one,
  instrumentsUsed: [Instrument.VOCAL],
});

يمكن للعملية التي يتم استدعاؤها بهذه الطريقة الوصول إلى قاعدة البيانات بشكل كامل. إذا كانت لديك طلبات بحث أو عمليات تغيير مخصصة للاستخدام لأغراض الإدارة فقط، عليك تحديدها باستخدام توجيه @auth(level: NO_ACCESS). يضمن ذلك ألا يتمكّن من تنفيذ هذه العمليات إلا المتصلون على مستوى المشرف.

إدارة البيانات باستخدام طرق executeGraphql

إذا كنت بحاجة إلى تنفيذ عمليات لمرة واحدة لم تحدّد لها gql عمليات تغيير أو طلبات بحث، يمكنك استخدام طريقة executeGraphql أو طريقة للقراءة فقط executeGraphqlRead.

إجراء عمليات علائقية مخصصة ومتداخلة (علاقة رأس بأطراف) باستخدام executeGraphql

عند استدعاء العمليات من حزمة Admin SDK في بيئة مميزة، ليس عليك تحديد توجيه @allow على المتغيّرات لعمليات الإدراج العادية المسطّحة (غير المتداخلة)، مثل إدراج سجلات فردية لـ "فيلم" أو "ممثل".

ومع ذلك، بالنسبة إلى عمليات الإدراج العلائقية المتداخلة (مثل إدراج فيلم مع مراجعاته الأولية)، عليك مع ذلك الإعلان عن توجيه @allow في تعريف المتغيّر للسماح بشكل ثابت بالحقول العلائقية المتداخلة داخل مخطط المتغيّر الذي تم تجميعه.

// Custom GraphQL mutation with nested relational insert
const customMutation = `
  mutation CustomBatchInsert(
    $data: [Movie_Data!]!
    @allow(fields: "title genre reviews_on_movie { rating reviewText }")
  ) {
    movie_insertMany(data: $data)
  }
`;
const variables = {
  data: [
    {
      title: "Interstellar",
      genre: "Sci-Fi",
      reviews_on_movie: [
        {
          rating: 5,
          reviewText: "Visually stunning and emotionally powerful.",
          user: { id: "user-789" }
        }
      ]
    }
  ]
};
const response = await dataConnect.executeGraphql(customMutation, { variables });

انتحال هوية مستخدم غير مصادَق عليه

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

// Query to get posts, with authentication level PUBLIC
const queryGetPostsImpersonation = `
    query getPosts @auth(level: PUBLIC) {
        posts {
          description
        }
    }`;

// Attempt to access data as an unauthenticated user
const optionsUnauthenticated: GraphqlOptions<undefined> = {
    impersonate: {
        unauthenticated: true
    }
};

// executeGraphql with impersonated unauthenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetPostsImpersonation, optionsUnauthenticated);

انتحال هوية مستخدم

هناك أيضًا حالات استخدام تريد فيها أن تعدّل النصوص البرمجية بيانات المستخدم استنادًا إلى بيانات اعتماد محدودة، نيابةً عن مستخدم معيّن. يتوافق هذا النهج مع مبدأ الحد الأدنى من الأذونات المميزة.

لاستخدام هذه الواجهة، اجمَع المعلومات من رمز مصادقة JWT مخصّص يتّبع تنسيق الرمز Authentication. راجِع أيضًا دليل الرموز المميّزة المخصّصة.

// Get the current user's data
const queryGetUserImpersonation = `
    query getUser @auth(level: USER) {
        user(key: {uid_expr: "auth.uid"}) {
            id,
            name
        }
    }`;

// Impersonate a user with the specified auth claims
const optionsAuthenticated: GraphqlOptions<undefined> = {
    impersonate: {
        authClaims: {
            sub: 'QVBJcy5ndXJ1'
        }
    }
};

// executeGraphql with impersonated authenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetUserImpersonation, optionsAuthenticated);

// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }

استخدام بيانات الاعتماد الإدارية

إذا كنت تجري عملية تتطلب أذونات على مستوى المشرف، احذف المَعلمة `impersonate` من الاستدعاء:

// User can be publicly accessible, or restricted to admins
const query = "query getProfile(id: AuthID) { user(id: $id) { id name } }";

interface UserData {
  user: {
    id: string;
    name: string;
  };
}

export interface UserVariables {
  id: string;
}

const options:GraphqlOptions<UserVariables> = { variables: { id: "QVBJcy5ndXJ1" } };

// executeGraphql
const gqlResponse = await dataConnect.executeGraphql<UserData, UserVariables>(query, options);

// executeGraphqlRead (similar to previous sample but only for read operations)
const gqlResponse = await dataConnect.executeGraphqlRead<UserData, UserVariables>(query, options);

// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }

يمكن للعملية التي يتم استدعاؤها بهذه الطريقة الوصول إلى قاعدة البيانات بشكل كامل. إذا كانت لديك طلبات بحث أو عمليات تغيير مخصصة للاستخدام لأغراض الإدارة فقط، عليك تحديدها باستخدام توجيه @auth(level: NO_ACCESS). يضمن ذلك ألا يتمكّن من تنفيذ هذه العمليات إلا المتصلون على مستوى المشرف.

إجراء عمليات إدراج البيانات المجمّعة

ننصحك باستخدام Admin SDK لإجراء عمليات البيانات المجمّعة على قواعد بيانات الإنتاج.

توفّر حزمة SDK الطرق التالية للتعامل مع البيانات المجمّعة. من الوسيطات المقدَّمة، تنشئ كل طريقة عملية تغيير GraphQL وتنفّذها.


// Methods of the bulk operations API
// dc is a SQL Connect admin instance from getDataConnect

const resp = await dc.insert("movie" /*table name*/, data[0]);
const resp = await dc.insertMany("movie" /*table name*/, data);
const resp = await dc.upsert("movie" /*table name*/, data[0]);
const resp = await dc.upsertMany("movie" /*table name*/, data);

العمليات العلائقية المتداخلة (علاقة رأس بأطراف)

تتوافق Admin SDK مع البيانات العلائقية في العمليات المجمّعة:

// Example of inserting a movie with nested reviews
const moviesData = [
  {
    title: "Interstellar",
    genre: "Sci-Fi",
    reviews_on_movie: [
      {
        rating: 5,
        reviewText: "Visually stunning and emotionally powerful.",
        user: { id: "user-789" }
      }
    ]
  }
];

// Atomically insert movies and their reviews in a single database round-trip.
const response = await dc.insertMany("movie", moviesData);

ملاحظات الأداء للعمليات المجمّعة

سيؤدي كل طلب يتم إرساله إلى الخلفية إلى رحلة واحدة إلى Cloud SQL، لذا كلما زاد عدد العمليات المجمّعة، زادت الإنتاجية.

ومع ذلك، كلما زاد حجم المجموعة، زاد طول عبارة SQL التي تم إنشاؤها. وعند الوصول إلى الحد الأقصى لطول عبارة PostgreSQL SQL، سيظهر لك خطأ.

من الناحية العملية، جرِّب أحجامًا مختلفة للمجموعات للعثور على الحجم المناسب لعبء العمل.

ما هي الخطوات التالية؟