Realtime Database ট্রিগার (ফার্স্ট জেনারেশন)

Cloud Functions-এর সাহায্যে, আপনি ক্লায়েন্ট কোড আপডেট করার প্রয়োজন ছাড়াই Firebase Realtime Database-এ ইভেন্ট ম্যানেজ করতে পারবেন। Cloud Functions আপনাকে সম্পূর্ণ অ্যাডমিনিস্ট্রেটিভ প্রিভিলেজ সহ Realtime Database অপারেশন রান করতে দেয় এবং Realtime Database-এ করা প্রতিটি পরিবর্তন আলাদা আলাদাভাবে প্রসেস করা হয়। আপনি DataSnapshot অথবা Admin SDK-এর মাধ্যমে Firebase Realtime Database পরিবর্তন করতে পারবেন।

সাধারণ লাইফসাইকেলে, Firebase Realtime Database ফাংশন নিম্নলিখিত কাজগুলি করে:

  1. নির্দিষ্ট Realtime Database লোকেশনে পরিবর্তনের জন্য অপেক্ষা করে।
  2. কোনও ইভেন্ট ঘটলে ট্রিগার হয় এবং নিজের টাস্ক পারফর্ম করে (Cloud Functions-এর সাহায্যে আমি কী করতে পারি? দেখুন) ব্যবহারের উদাহরণ)
  3. নির্দিষ্ট ডকুমেন্টে সেভ করা ডেটার স্ন্যাপশট থাকা ডেটা অবজেক্ট পায়।

Realtime Database ফাংশন ট্রিগার করা

functions.database-এর সাহায্যে Realtime Database ইভেন্টের জন্য নতুন ফাংশন তৈরি করুন। ফাংশন কখন ট্রিগার হবে তা নিয়ন্ত্রণ করতে, ইভেন্ট হ্যান্ডলারের মধ্যে একটি নির্দিষ্ট করুন এবং Realtime Database পাথ নির্দিষ্ট করুন যেখানে এটি ইভেন্টের জন্য শুনবে।

ইভেন্ট হ্যান্ডলার সেট করা

ফাংশন আপনাকে Realtime Databaseদুটি লেভেলে নির্দিষ্ট ইভেন্ট ম্যানেজ করতে দেয়; আপনি শুধুমাত্র তৈরি করা, আপডেট করা, বা মুছে ফেলা ইভেন্ট শুনতে পারেন অথবা কোনও পাথে যেকোনও ধরনের পরিবর্তন শুনতে পারেন। Cloud Functions-এ Realtime Database-এর জন্য এইসব ইভেন্ট হ্যান্ডলার কাজ করে:

  • onWrite(), যা Realtime Database-এ ডেটা তৈরি, আপডেট বা মুছে ফেলা হলে ট্রিগার করে।
  • onCreate(), যা Realtime Database-এ নতুন ডেটা তৈরি হলে ট্রিগার হয়।
  • onUpdate(), যা Realtime Database-এ ডেটা আপডেট করা হলে ট্রিগার হয়।
  • onDelete(), যা Realtime Database থেকে ডেটা মুছে দিলে ট্রিগার হয়।

ইনস্ট্যান্স ও পাথ নির্দিষ্ট করুন

আপনার ফাংশন কখন ও কোথায় ট্রিগার করা উচিত তা নিয়ন্ত্রণ করতে, ref(path) একটি পাথ নির্দিষ্ট করতে কল করুন এবং ঐচ্ছিকভাবে instance('INSTANCE_NAME')-এর সাথে Realtime Database ইনস্ট্যান্স নির্দিষ্ট করুন। আপনি কোনও ইনস্ট্যান্স নির্দিষ্ট না করলে, ফাংশনটি Firebase প্রোজেক্টের জন্য ডিফল্ট Realtime Database ইনস্ট্যান্সে ডেপ্লয় করা হয়। যেমন:

  • ডিফল্ট Realtime Database ইনস্ট্যান্স: functions.database.ref('/foo/bar')
  • "my-app-db-2" নামের ইনস্ট্যান্স: functions.database.instance('my-app-db-2').ref('/foo/bar')

এইসব পদ্ধতি আপনার ফাংশনকে Realtime Database ইনস্ট্যান্সের মধ্যে নির্দিষ্ট পাথে রাইট হ্যান্ডেল করার নির্দেশ দেয়। পাথ স্পেসিফিকেশন সব রাইটের সাথে ম্যাচ করে যা কোনও পাথকে স্পর্শ করে, এর মধ্যে এমন রাইটও আছে যা এর নিচে যেকোনও জায়গায় ঘটে। আপনি যদি নিজের ফাংশনের জন্য পাথ /foo/bar হিসেবে সেট করেন, তাহলে এটি এই দুটি লোকেশনেই ইভেন্টের সাথে ম্যাচ করে:

 /foo/bar
 /foo/bar/baz/really/deep/path

দুটি ক্ষেত্রেই, Firebase ধরে নেয় যে ইভেন্টটি /foo/bar-এ ঘটেছে এবং ইভেন্ট ডেটাতে /foo/bar-এ পুরনো ও নতুন ডেটা অন্তর্ভুক্ত থাকে। ইভেন্ট ডেটা অনেক বেশি হলে, আপনার ডেটাবেসের রুট ফোল্ডারের কাছাকাছি একটি ফাংশনের পরিবর্তে আরও গভীর পাথে একাধিক ফাংশন ব্যবহার করার কথা বিবেচনা করুন। সবচেয়ে ভালো পারফর্ম্যান্সের জন্য, যতটা সম্ভব গভীর লেভেলে ডেটা অনুরোধ করুন।

আপনি কার্লি ব্র্যাকেট দিয়ে ঘিরে কোনও পাথ কম্পোনেন্টকে ওয়াইল্ডকার্ড হিসেবে নির্দিষ্ট করতে পারেন; ref('foo/{bar}') /foo-এর যেকোনও চাইল্ডের সাথে ম্যাচ করে। এইসব ওয়াইল্ডকার্ড পাথ কম্পোনেন্টের ভ্যালু আপনার ফাংশনের EventContext.params অবজেক্টের মধ্যে উপলভ্য। এই উদাহরণে, ভ্যালু উপলভ্য আছে এইভাবে context.params.bar।

ওয়াইল্ডকার্ড সহ পাথ একটি রাইট থেকে একাধিক ইভেন্টের সাথে ম্যাচ করতে পারে। এর একটি ইনসার্ট

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

"/foo/{bar}" পাথের সাথে দু'বার ম্যাচ করেছে: একবার "hello": "world" এবং আবার "firebase": "functions"-এর সাথে।

ইভেন্ট ডেটা ম্যানেজ করা

Realtime Database ইভেন্ট হ্যান্ডেল করার সময়, রিটার্ন করা ডেটা অবজেক্ট হল DataSnapshot। onWrite বা onUpdate ইভেন্টের ক্ষেত্রে, প্রথম প্যারামিটার হল একটি Change অবজেক্ট যাতে দুটি স্ন্যাপশট থাকে যা ট্রিগার করা ইভেন্টের আগে ও পরে ডেটার অবস্থা দেখায়। onCreate এবং onDelete ইভেন্টের জন্য, ডেটা অবজেক্ট রিটার্ন করা হল তৈরি বা মুছে ফেলা ডেটার স্ন্যাপশট।

এই উদাহরণে, ফাংশনটি নির্দিষ্ট পাথের জন্য স্ন্যাপশট পুনরুদ্ধার করে, সেই লোকেশনে থাকা স্ট্রিংটিকে বড় হাতের অক্ষরে পরিবর্তন করে এবং সেই পরিবর্তিত স্ট্রিংটি ডেটাবেসে লেখে:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

ব্যবহারকারীর পরিচয় যাচাইকরণ সংক্রান্ত তথ্য অ্যাক্সেস করা

EventContext.auth ও EventContext.authType থেকে, আপনি ফাংশন ট্রিগার করেছেন এমন ব্যবহারকারীর অনুমতি সহ ব্যবহারকারীর তথ্য অ্যাক্সেস করতে পারবেন। এটি নিরাপত্তা সংক্রান্ত নিয়ম প্রয়োগ করার জন্য, ব্যবহারকারীর অনুমতির লেভেলের উপর ভিত্তি করে আপনার ফাংশনকে বিভিন্ন অপারেশন সম্পূর্ণ করার অনুমতি দেওয়ার জন্য সহায়ক হতে পারে:

const functions = require('firebase-functions/v1');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

এছাড়াও, আপনি ব্যবহারকারীর পরিচয় যাচাইকরণ সংক্রান্ত তথ্য ব্যবহার করে কোনও ব্যবহারকারীর ছদ্মবেশ ধারণ করতে এবং তার হয়ে ডেটা লেখার অপারেশন পারফর্ম করতে পারবেন। কনকারেন্সি সংক্রান্ত সমস্যা এড়াতে, নিচে দেখানো অ্যাপ ইনস্ট্যান্সটি মুছে দিতে ভুলবেন না:

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

আগের ভ্যালু পড়ুন

Change অবজেক্টে একটি before প্রপার্টি আছে যা আপনাকে ইভেন্টের Realtime Database আগে সেভ করা আইটেম চেক করতে দেয়। before প্রপার্টি এমন একটি DataSnapshot রিটার্ন করে যেখানে সব মেথড (যেমন, val() এবং exists()) আগের ভ্যালুকে রেফার করে। আপনি আসল DataSnapshot ব্যবহার করে অথবা after প্রপার্টি পড়ে নতুন ভ্যালু আবার পড়তে পারবেন। যেকোনও Change-এর এই প্রপার্টি হল আরেকটি DataSnapshot যা ইভেন্ট ঘটার পরে ডেটার অবস্থা দেখায়।

যেমন, before প্রপার্টি ব্যবহার করে এটি নিশ্চিত করা যেতে পারে যে ফাংশনটি টেক্সট প্রথম তৈরি করার সময় শুধুমাত্র বড় হাতের অক্ষর ব্যবহার করবে:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return change.after.ref.parent.child('uppercase').set(uppercase);
    });