ফায়ারবেস এক্সটেনশনগুলিকে ক্লাউড ফাংশনে স্থানান্তর করুন

এই নির্দেশিকাটি আপনাকে দেখাবে কীভাবে আপনার এক্সটেনশনগুলিকে অপ্রচলিত Firebase Extensions পরিবেশ থেকে এমন একটি ফাংশনে স্থানান্তর করবেন, যা আপনার ব্যবহারকারীরা তাদের নিজস্ব Cloud Functions for Firebase (2nd gen) কোডবেসে ইনস্টল ও স্থাপন করতে পারে।

এটিই প্রস্তাবিত মাইগ্রেশন পথ। Firebase তার অফিসিয়াল এনপিএম সমতুল্য এক্সটেনশনগুলির একটি তালিকা বজায় রাখবে; এই নির্দেশিকাটি আপনাকে আপনার নিজেরটি তৈরি করার প্রক্রিয়াটি ধাপে ধাপে দেখাবে।

এই নির্দেশিকা জুড়ে, স্ট্রিম Cloud Firestore টু BigQuery এক্সটেনশন ( firestore-bigquery-export ) একটি চলমান উদাহরণ হিসেবে ব্যবহৃত হয়েছে। প্রতিটি বিভাগের শেষে একটি সমাধান করা উদাহরণ রয়েছে, যা দেখায় যে মাইগ্রেশনের আগে এক্সটেনশনটি কেমন ছিল এবং @firebase-function-kits/firestore-bigquery-export প্যাকেজ হিসেবে মাইগ্রেশনের পরে এটি কেমন দেখায়।

এক্সটেনশন মাইগ্রেট করার বিষয়ে আরও তথ্য ও সাহায্য পেতে সাইন আপ করুন।

Firebase Extensions থেকে মাইগ্রেট করার পদ্ধতি সম্পর্কে আপনার কোনো প্রশ্ন থাকলে, আপনি firebase-extensions-migrator-support-external@google.com এই ঠিকানায় আমাদের সাথে যোগাযোগ করতে পারেন। আপনার ২য় প্রজন্মের ফাংশনগুলোর প্যাকেজিং, টেস্টিং এবং ডিস্ট্রিবিউশন বিষয়ে আরও তথ্য দিয়ে আমরা যখন গাইডটি আপডেট করব, তখন এই গ্রুপেও ইমেল করব।

এই গ্রুপে যোগ দিতে, firebase-extensions-migrator-support-external+subscribe@google.com- এ একটি বার্তা পাঠান, যার উত্তরে একটি সদস্যপদ অনুরোধের ইমেল পাঠানো হবে। আপনাকে অবশ্যই সেই ইমেলের উত্তর দিতে হবে, "এই গ্রুপে যোগ দিন" বোতামে ক্লিক করা যাবে না।

শুরু করার আগে

এই মাইগ্রেশনটি নির্দেশিতভাবে সম্পন্ন করতে, আপনি Cloud Functions এর নিম্নলিখিত বৈশিষ্ট্যগুলি ব্যবহার করবেন:

  • প্যারামিটারযুক্ত কনফিগারেশন । extension.yaml এ আপনি যে প্রতিটি 'param' ঘোষণা করেন, তা আপনার প্যাকেজ কোডে একটি সংজ্ঞায়িত প্যারামিটার হয়ে যায়।

  • ডিক্লারেটিভ IAM রোল এবং প্রয়োজনীয় API। extension.yaml এ আপনার ঘোষিত প্রতিটি রোল আপনার প্যাকেজ কোডে একটি requiresRole(...) কলে এবং প্রতিটি API একটি requiresAPI(...) কলে পরিণত হয়। ডিপ্লয় করার সময়, Firebase CLI আপনার পক্ষ থেকে একটি ম্যানেজড রানটাইম সার্ভিস অ্যাকাউন্টে ঘোষিত রোলগুলো প্রদান করে এবং ঘোষিত API-গুলো সক্রিয় করে।

  • Cloud Functions কোডবেসের জন্য লাইফসাইকেল ইভেন্ট। Cloud Functions কোডবেস এখন Firebase Extensions অনুরূপ লাইফসাইকেল ইভেন্ট সমর্থন করে। afterFirstDeploy(...) এবং afterRedeploy(...) লাইফসাইকেল হুক ব্যবহার করে ইনস্টল-টাইম এবং আপডেট-টাইম সেটআপ ঘোষণা করুন। এগুলি extension.yaml এ আপনার ঘোষিত lifecycleEvents প্রতিস্থাপন করে।

Firebase Extensions সোর্সকে একটি দ্বিতীয় প্রজন্মের ফাংশনে স্থানান্তর করুন

(ঐচ্ছিক) Firebase এজেন্ট স্কিলের মাধ্যমে স্বয়ংক্রিয় মাইগ্রেশন

আপনি অফিসিয়াল extension-to-functions-codebase এআই এজেন্ট স্কিলটি ব্যবহার করে ধাপ ১ থেকে ৮ (রিসোর্সের ইনভেন্টরি তৈরি, ট্রিগার আপগ্রেড, প্যারাম ও সিক্রেট রূপান্তর, ডিক্লারেটিভ আইএএম, লাইফসাইকেল হুক এবং প্যাকেজ রিডমি তৈরি) স্বয়ংক্রিয় করতে পারেন।

দক্ষতা ইনস্টল করুন

যদি আপনি বা আপনার এআই কোডিং অ্যাসিস্ট্যান্ট ( Firebase জেমিনি, কার্সর, ক্লড কোড, গিটহাব কপাইলট) এখনও স্কিলটি ইনস্টল না করে থাকেন, তাহলে স্কিলের সিএলআই (CLI) ব্যবহার করে নিম্নলিখিত কমান্ডটি চালান:

npx skills add firebase/agent-skills --skill extension-to-functions-codebase

আপনার প্রোজেক্টে স্কিলটি ইনস্টল হয়ে গেলে, আপনার এআই কোডিং অ্যাসিস্ট্যান্ট স্বয়ংক্রিয়ভাবে এর মাইগ্রেশন নিয়ম এবং রূপান্তরের ধাপগুলো অনুসরণ করে। আপনি নিম্নলিখিত প্রম্পটটি ব্যবহার করতে পারেন:

অনুগ্রহ করে extension-to-functions-codebase ' স্কিলের নির্দেশাবলী অনুসরণ করে এই Firebase এক্সটেনশনটিকে একটি প্রকাশযোগ্য ২য় প্রজন্মের ফাংশন কিট প্যাকেজে স্থানান্তর করুন।

১. সম্প্রসারণের তালিকা তৈরি করুন

আপনার এক্সটেনশনটির একটি সম্পূর্ণ তালিকা তৈরি করার মাধ্যমে শুরু করুন: এক্সটেনশনটি যা কিছু ঘোষণা করে, সরবরাহ করে এবং নথিভুক্ত করে, তার একটি পূর্ণাঙ্গ তালিকা তৈরি করুন, যাতে দ্বিতীয় প্রজন্মের ফাংশনে প্রতিটি আচরণের একটি নির্দিষ্ট গন্তব্য থাকে এবং মাইগ্রেশনের সময় কিছুই হারিয়ে না যায়।

নিচের প্রতিটি বিষয় পর্যালোচনা করুন এবং আপনি যা খুঁজে পান তা লিপিবদ্ধ করুন:

  • extension.yaml , যা আপনার প্যারামিটার, ফাংশন, ইভেন্ট, IAM রোল, প্রয়োজনীয় API, সিক্রেট এবং লাইফসাইকেল হুক ঘোষণা করে।

  • functions/ ফোল্ডারে আপনার ফাংশন কোড, ডিপেন্ডেন্সি, বিল্ড কনফিগারেশন, ট্রিগার এবং টাস্ক কিউ ফাংশনগুলো থাকে।

  • README.md , PREINSTALL.md , এবং POSTINSTALL.md , যেগুলিতে সেটআপের ধাপ, সতর্কতা এবং বিলিং সংক্রান্ত নোট রয়েছে।

  • scripts/ ফোল্ডারে যেকোনো ইম্পোর্ট, ব্যাকফিল, আইএএম, রিপেয়ার বা মাইগ্রেশন ইউটিলিটি এবং এক্সটেনশনটির সাথে আপনি সরবরাহ করা অন্য যেকোনো টুলিং থাকে।

তারপর, extension.yaml এর প্রতিটি আইটেমের জন্য, npm প্যাকেজে সেটি কোথায় যাবে তা স্থির করুন:

  • ব্যবহারকারীর কনফিগারেশনকে Cloud Functions প্যারামিটারে রূপান্তর করুন ( ধাপ ৪ )।

  • সিক্রেটগুলোকে Cloud Functions সিক্রেটে রূপান্তর করুন ( ধাপ ৪ )।

  • IAM রোলগুলিকে requiresRole(...) ডিক্লারেশনে রূপান্তর করুন ( ধাপ 6 )।

  • প্রয়োজন অনুযায়ী প্রয়োজনীয় Google API-গুলোকে requiresAPI(...) ডিক্লারেশনে রূপান্তর করুন ( ধাপ 6 )।

  • install এবং update হুকগুলিকে afterFirstDeploy(...) এবং afterRedeploy(...) ডিক্লারেশনে রূপান্তর করুন ( ধাপ ৭ )।

  • ইনস্ট্যান্স আইডি EXT_INSTANCE_ID থেকে FIREBASE_KIT_INSTANCE_ID তে রূপান্তর করুন ( ধাপ ৪ )।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

firestore-bigquery-export/extension.yaml এবং functions/ পড়লে এই ইনভেন্টরি পাওয়া যায়:

extension.yaml এ গণনা / মান যেখানে এটা যায়
params ২৫ ( COLLECTION_PATH , DATASET_ID , TABLE_ID , DATASET_LOCATION , VIEW_TYPE , …) Cloud Functions প্যারামিটারসমূহ ( ধাপ ৪ )
apis bigquery.googleapis.com requiresAPI(...) ( ধাপ ৬ )
roles bigquery.dataEditor , datastore.user , bigquery.user requiresRole(...) ( ধাপ ৬ )
resources ১টি ইভেন্ট ট্রিগার ( fsexportbigquery ) + টাস্ক কিউ ফাংশন ( initBigQuerySync , setupBigQuerySync ) রপ্তানিকৃত প্যাকেজ ফাংশন ( ধাপ ৩ )
lifecycleEvents onInstall → initBigQuerySync ; onUpdate / onConfigure → setupBigQuerySync afterFirstDeploy / afterRedeploy ( ধাপ ৭ )
ইনস্ট্যান্স আইডি ব্যবহৃত হয়নি (কোনো EXT_INSTANCE_ID রিড করা হয়নি) স্থানান্তরের কিছুই নেই
scripts/ import/ (ব্যাকফিল), gen-schema-view/ স্ক্রিপ্ট হিসেবে রাখা হয়েছে (এখানে আলোচনার আওতার বাইরে)

বিশ্লেষণ। এক্সটেনশনটি কোনো type: secret প্যারামিটার ঘোষণা করে না, তাই ধাপ ৪- এ secret-এর জন্য মাইগ্রেট করার কিছু নেই। ইভেন্ট ট্রিগারটি ইতিমধ্যেই ২য় প্রজন্মের; শুধুমাত্র টাস্ক কিউ ফাংশনগুলো এখনও ১ম প্রজন্মের (যা ধাপ ৩-এর জন্য প্রাসঙ্গিক)।

২. package.json আপডেট করুন

আপনার এক্সটেনশনের package.json ফাইলটি আপডেট করুন। আপনি যদি একটি এক্সটেনশন মাইগ্রেট করেন, তবে এটিই রুট package.json হতে পারে। আর যদি একটি রিপোজিটরির মধ্যে অনেকগুলো এক্সটেনশন মাইগ্রেট করেন, তবে প্রতিটি এক্সটেনশনকে তার নিজস্ব প্যাকেজ দিন।

ন্যূনতম SDK সংস্করণ: firebase-functions >= 7.4.0 এবং firebase-admin >= 14.2.0 কে ডিপেন্ডেন্সি হিসেবে ঘোষণা করুন। আপনার firebase-functions সংস্করণটিকেও একটি পিয়ার ডিপেন্ডেন্সি হিসেবে ঘোষণা করুন, যাতে আপনার ব্যবহারকারীদের Cloud Functions প্রজেক্টে সেই একই SDK সংস্করণ থাকে যা দিয়ে আপনার লাইব্রেরিটি লেখা হয়েছে।

{
  "name": "<package-name>",
  "version": "1.0.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "firebase-functions": "^7.4.0",
    "firebase-admin": "^14.2.0"
  }
}

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

পূর্বে। এক্সটেনশনটির functions/package.json ফাইলটি private, এতে এক্সটেনশন আইডি-র নাম উল্লেখ করা আছে, এবং firebase-functions একটি সরাসরি ডিপেন্ডেন্সি হিসেবে ঘোষণা করা হয়েছে:

{
  "name": "firestore-bigquery-export",
  "main": "lib/index.js",
  "private": true,
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^6.3.2"
  }
}

পরে। একটি প্রকাশযোগ্য প্যাকেজ: স্কোপড নাম, একটি exports ম্যাপ, এবং firebase-functions peerDependencies এ স্থানান্তরিত হয়েছে:

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.1.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^7.4.0"
  }
}

৩. প্রথম প্রজন্ম থেকে দ্বিতীয় প্রজন্মে ফাংশন আপগ্রেড করুন

আপনার এক্সটেনশনটি যদি এখনও প্রথম প্রজন্মের ফাংশন এক্সপোর্ট করে, তবে প্রতিটি ট্রিগারকে তার দ্বিতীয় প্রজন্মের সমতুল্য রূপে রূপান্তর করুন। ` firebase-functions/... মডিউলগুলো থেকে ইম্পোর্ট করুন এবং ট্রিগার অপশনে রানটাইম সেটিংস পাস করুন।

Cloud Functions ২য় জেনারেশন আপগ্রেড গাইডটি দেখুন। বিশেষভাবে উল্লেখ্য, আপনি ২য় জেনারেশনের প্যাচ করা ইভেন্ট ডিস্ট্রাকচারিং ব্যবহার করে কোড পুনর্লিখনের প্রচেষ্টা কমাতে পারেন এবং আপনার ফাংশন লজিক পুনরায় লেখা এড়াতে পারেন, কারণ ২য় জেনারেশনের SDK, v1 প্যারামিটারগুলোকে ইভেন্ট অবজেক্টের ফিল্ড হিসেবে প্রকাশ করে, যা আপনাকে ডিস্ট্রাকচার্ড/নেমড প্যারামিটার ব্যবহার করতে এবং আপনার বিজনেস লজিক অপরিবর্তিত রাখতে সাহায্য করে।

পূর্বে। প্রথম প্রজন্ম:

import * as functions from "firebase-functions/v1";

export const sync = functions.firestore
  .document("{collectionId}/{documentId}")
  .onWrite(async (change, context) => {
    await handleWrite(change.before, change.after, context.params);
  });

পরে। দ্বিতীয় প্রজন্ম:

import { onDocumentWritten } from "firebase-functions/firestore";

export const syncV2 = onDocumentWritten(
  { document: "{collectionId}/{documentId}" },
  async ({ change, context }) =>
    await handleWrite(change.before, change.after, context.params)
);

১ম প্রজন্ম এবং ২য় প্রজন্মের ফাংশনগুলোর মধ্যে পার্থক্যের একটি বিশদ তালিকার জন্য Cloud Functions সংস্করণ তুলনা দেখুন।

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

আপনার ব্যবহারকারীদের দ্বারা নির্বাচিত যেকোনো Cloud Functions লোকেশন মাইগ্রেশনের সময় অকার্যকর হয়ে যেতে পারে, কারণ বিদ্যমান লোকেশনটি সংরক্ষিত থাকে। যদি আপনার এক্সটেনশনের ইভেন্ট-ট্রিগারড ফাংশনগুলো ব্যবহারকারীর সরবরাহ করা ফাংশন লোকেশনের উপর নির্ভরশীল হয়, তাহলে আমরা আপনার এক্সটেনশনে একটি নতুন প্যারামিটার যোগ করার পরামর্শ দিচ্ছি, যা ইভেন্ট ট্রিগার লোকেশন সংগ্রহ করবে এবং এটিকে নতুন ফাংশন লোকেশনের সাথে ম্যাপ করবে।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

defineString("DATABASE_REGION", {
  label: "Firestore Instance Location",
  description:
    "Where is the Firestore database located? You can check your current database location at https://console.cloud.google.com/firestore/databases. The functions in this kit deploy to the Cloud Run region closest to this location.",
  input: select({
    "Multi-region (Europe - Belgium and Netherlands)": "eur3",
    "Multi-region (United States)": "nam5",
    "Multi-region (Iowa, North Virginia, and Oklahoma)": "nam7",
    "Iowa (us-central1)": "us-central1",
    // More locations...
  })
});

// Firestore multi-region locations are not Cloud Run regions; deploying a
// function to one hard-fails, so they map to a region inside the multi-region.
const MULTI_REGION_TO_FUNCTION_REGION: Record<string, string> = {
  nam5: "us-central1",
  nam7: "us-central1",
  eur3: "europe-west1",
};

/**
 * Maps a Firestore database location to the Cloud Run region the functions
 * should deploy to. The lookup is case-insensitive and ignores surrounding
 * whitespace, as the CLI's own region handling is. Regional locations pass
 * through lowercased; an unset or blank location returns `undefined`, meaning
 * the functions declare no region.
 */
export function firestoreLocationToFunctionRegion(
  location: string | undefined
): string | undefined {
  const normalized = location?.trim().toLowerCase();
  if (!normalized) {
    return undefined;
  }
  return MULTI_REGION_TO_FUNCTION_REGION[normalized] ?? normalized;
}

const functionRegion = firestoreLocationToFunctionRegion(
  process.env.DATABASE_REGION
);

export const fsexportbigquery = onDocumentWritten(
  {
    region: functionRegion,
    // Other configuration
  },
  (event) => handleDocumentWrite(event, getHandlerContext())
);

কিটটি প্রথমবার স্থাপন করার সময়, ব্যবহারকারীদের কাছে তাদের DATABASE_REGION জানতে চাওয়া হবে এবং ফাংশনগুলি উপরে উল্লিখিত সংশ্লিষ্ট functionRegion এ স্থাপন করা হবে, যা তাদের gen2 ইভেন্ট ট্রিগারড ফাংশনগুলির অবস্থান সংক্রান্ত যেকোনো সমস্যা সমাধান করবে।

৪. এক্সটেনশন প্যারামিটার এবং সিক্রেট রূপান্তর করুন

পরমস

extension.yaml এ আপনি যে প্রতিটি প্যারামিটার ঘোষণা করেন, তা একটি Cloud Functions প্যারামিটার হয়ে যায়।

সরাসরি পরিবেশ পাঠ রূপান্তর করুন:

const collectionPath = process.env.COLLECTION_PATH;

Cloud Functions প্যারামিটারগুলিতে:

import { defineString } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const collectionPath = defineString("COLLECTION_PATH");

// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
  { document: collectionPath },
  async (event) => {
    // Call .value() to read the string inside a handler
    const path = collectionPath.value();
    await handleWrite(path, event);
  }
);

হ্যান্ডলারের ভিতরে স্ট্রিং পড়ার জন্য collectionPath.value() ব্যবহার করুন; যেখানে প্লেসহোল্ডার প্রত্যাশিত, যেমন ফাংশন ট্রিগার পাথ, সেখানে সরাসরি collectionPath ব্যবহার করুন।

Firebase CLI আপনার প্যারামিটারগুলো খুঁজে বের করে এবং .env বা .env.<projectId> থেকে সেগুলোর মান পড়ে নেয়, অথবা ডিপ্লয় করার সময় আপনার ব্যবহারকারীদের কাছে জানতে চায়। প্যারামিটারের নামগুলো একই রাখুন, যাতে বিদ্যমান ইনস্টলেশনের মানগুলো স্থানান্তরিত হয়।

এটা গুরুত্বপূর্ণ যে আপনি আপনার কোডে ঘোষিত প্যারামিটারের নামগুলো কোনোভাবেই পরিবর্তন করবেন না । এক্সটেনশন মাইগ্রেশন বিদ্যমান এন্ড-ইউজার প্যারামিটারের মানগুলো স্বয়ংক্রিয়ভাবে সংরক্ষণ করে, কিন্তু শুধুমাত্র তখনই যখন নামগুলো অপরিবর্তিত থাকে।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

পূর্বে, extension.yaml এ ঘোষিত একটি প্যারামিটার, config.ts এ একটি সাধারণ এনভায়রনমেন্ট ভেরিয়েবল হিসেবে পঠিত হতো:

# extension.yaml
- param: COLLECTION_PATH
  label: Collection path
  type: string
  required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,

একটি defineString পরে ; CLI এটি খুঁজে বের করে এবং .env থেকে পড়ে:

// src/config.ts
import { defineString } from "firebase-functions/params";

collectionPath: defineString("COLLECTION_PATH", {
  label: "Collection path",
  // We now support "nonEmpty: true" to ensure a value other than the empty string
  // is entered, analogous to "required: true" in extension.yaml
  input: { text: { nonEmpty: true } }
}),

প্যারামিটারের নাম অপরিবর্তিত থাকে, তাই বিদ্যমান .env ফাইলটি কাজ করতে থাকে।

ইনস্ট্যান্স আইডি

এক্সটেনশনগুলো তাদের ইনস্ট্যান্স আইডি EXT_INSTANCE_ID থেকে গ্রহণ করে, যা এক্সটেনশন রানটাইম দ্বারা ইনজেক্ট করা হয়। ফাংশন কিটগুলো তাদের ইনস্ট্যান্স আইডি FIREBASE_KIT_INSTANCE_ID থেকে গ্রহণ করে, যা Firebase CLI প্রতিটি কিট ইনস্ট্যান্সের জন্য firebase.json এর instances ম্যাপে ইনস্ট্যান্সের কী-তে সেট করে দেয়। CLI এই আইডিটি ডিপ্লয়-টাইম ডিসকভারির সময়, এমুলেটরে এবং ডিপ্লয় করা ফাংশনগুলোতে সরবরাহ করে থাকে।

ইনস্ট্যান্স আইডি কোনো প্যারামিটার নয়, তাই এটিকে defineString দিয়ে ডিক্লেয়ার করবেন না। প্রকৃতপক্ষে, FIREBASE_... হলো .env ফাইলগুলিতে একটি সংরক্ষিত প্রিফিক্স, তাই ব্যবহারকারীরা সেখানে এটি সেট বা ওভাররাইড করতে পারবেন না। CLI যে ভ্যালুগুলো ইনজেক্ট করে, সেগুলো প্যারামিটার সিস্টেমের কাছে দৃশ্যমান নয়। এটি সরাসরি এনভায়রনমেন্ট থেকে পড়ুন:

// Before
const instanceId = process.env.EXT_INSTANCE_ID;

// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

ভেরিয়েবলটি শুধুমাত্র তখনই সেট করা হবে যখন আপনার প্যাকেজটি একটি কিট হিসাবে ডেপ্লয় করা হবে। যদি আপনার কোড একটি স্বতন্ত্র কোডবেস হিসাবেও ডেপ্লয় করা হয় ( ধাপ ৯ দেখুন), তবে এটিকে ঐচ্ছিক হিসাবে বিবেচনা করুন অথবা এটি অনুপস্থিত থাকলে একটি স্পষ্ট বার্তা দিয়ে দ্রুত ব্যর্থ হওয়ার ব্যবস্থা নিন। যদি আপনার এক্সটেনশনটি ইনস্ট্যান্স আইডি-কে একটি ব্যবহারকারী-মুখী প্যারামিটার হিসাবে প্রকাশ করে থাকে, তবে আপনাকে অবশ্যই সেই প্যারামিটারটি সরিয়ে ফেলতে হবে, কারণ এখন ফায়ারবেস সিএলআই Firebase CLI) এই মানটির মালিক।

সমাধানকৃত উদাহরণ: ব্যবহারকারীর ডেটা মুছে ফেলুন

(Stream Cloud Firestore to BigQuery এক্সটেনশনটি তার ইনস্ট্যান্স আইডি পড়ে না, তাই সেখানে মাইগ্রেট করার মতো কিছু নেই। Delete User Data এক্সটেনশনটি তার পাব/সাব টপিকগুলোর নামকরণের জন্য এটি ব্যবহার করে।)

পূর্বে। config.ts এ ext- প্রিফিক্স সহ একটি র' এনভায়রনমেন্ট ভেরিয়েবল হিসেবে পঠিত হতো, যা এক্সটেনশনগুলো তার রিসোর্সগুলোর জন্য ব্যবহার করত:

// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,

এরপরে, দুটি সাধারণ প্যারামিটারের জন্য FIREBASE_KIT_INSTANCE_ID এর একটি সাধারণ process.env রিড ব্যবহার করা হয়, যাতে ব্যবহারকারীরা টপিকের নামগুলো ওভাররাইড করতে পারেন:

// src/config.ts
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
  default: `kit-${instanceId}-discovery`,
}),
deletionTopicName: defineString("DELETION_TOPIC_NAME", {
  default: `kit-${instanceId}-deletion`,
}),

ডিফল্টগুলো অবশ্যই খালি হওয়া চলবে না, কারণ ডিসকভারির সময়ে ট্রিগার বাইন্ডিংগুলো রিজলভ হয়। একটি খালি ডিফল্টকে ডিপ্লয় ম্যানিফেস্টে টপিকের নাম হিসেবে লেখা হবে। কিটটি তার নিজস্ব কনটেক্সটের বাইরে চলার বিরুদ্ধেও প্রতিরক্ষামূলক ব্যবস্থা নেয়। যদি ভ্যারিয়েবলটি অনুপস্থিত থাকে, তাহলে মডিউল-লেভেলের ডিফল্টটি kit-undefined-discovery হিসেবে ইভ্যালুয়েট হবে, ফলে কনফিগ লোডারটি একটি ব্যাখ্যাসহ এরর দেখিয়ে ফেইল করবে:

// ...
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
if (!instanceId) {
  throw new Error(
    "FIREBASE_KIT_INSTANCE_ID is not set. It is provided automatically to " +
      "kit instances by firebase-tools >= 15.32.0; deploy or emulate this " +
      "kit with a supported CLI version."
  );
}
// ...

যখন কোনো হ্যান্ডলার প্রথমবার তার কনফিগারেশন রিজলভ করে, তখন এই চেকটি চলে। ফলে, ফাংশনগুলো নীরবে kit-undefined-* টপিকের সাথে আবদ্ধ না হয়ে, একটি অনুপস্থিত ভ্যারিয়েবল একটি সুস্পষ্ট রানটাইম এরর তৈরি করে। ডিপ্লয়মেন্টটি বাতিল করতে, চেকটি মডিউল স্কোপে সম্পাদন করুন, যাতে এটি ডিসকভারির সময় চলে। যেহেতু CLI firebase.json থেকে ইনস্ট্যান্স আইডি নেয়, তাই কোনো কনফিগারযোগ্য INSTANCE_ID থাকে না এবং একাধিক ইনস্ট্যান্সের মধ্যে সিঙ্ক করে রাখার মতো কিছু নেই।

গোপনীয়তা

extension.yaml ফাইলে, আপনি type: secret ব্যবহার করে সিক্রেটগুলো ডিক্লেয়ার করেন। এক্সটেনশন রানটাইম এগুলো স্টোর ও বাইন্ড করে রাখে, ফলে আপনার এক্সটেনশন কোড সরাসরি process.env.PARAM_NAME পড়তে পারে। একটি সাধারণ Cloud Functions কোডবেসে, আপনি প্রতিটি সিক্রেট স্পষ্টভাবে ডিক্লেয়ার এবং বাইন্ড করেন:

import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";

const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);

আপনার এক্সটেনশনটি একবার কোনো এনপিএম প্যাকেজ/কিটে স্থানান্তরিত হয়ে গেলে, সিক্রেট রেফারেন্সগুলো শেষ ব্যবহারকারীর .env ফাইলে পরিচালিত হয়। এটা গুরুত্বপূর্ণ যে আপনি আপনার কোডে ঘোষিত সিক্রেটের নামগুলো কোনোভাবেই পরিবর্তন করবেন না । স্থানান্তরের সময়, শেষ ব্যবহারকারীর সিক্রেটগুলো সেই অনুযায়ী স্থানান্তরিত হয়।

কার্যকরী উদাহরণ: Cloud Firestore থেকে ইমেল ট্রিগার করা

পূর্বে, config.ts এ MAIL_COLLECTION এবং SMTP_PASSWORD সরাসরি এনভায়রনমেন্ট ভেরিয়েবল হিসেবে পঠিত হতো:

# extension.yaml
- param: MAIL_COLLECTION
  label: Email documents collection
  type: string
  default: mail
  required: true

- param: SMTP_PASSWORD
  label: SMTP password
  type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,

এরপরে। একটি defineString এবং একটি defineSecret ; CLI উভয়কেই খুঁজে বের করে এবং .env থেকে পড়ে:

import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const mailCollection = defineString("MAIL_COLLECTION", {
  label: "Email documents collection",
  default: "mail"
});

const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });

export const processQueue = onDocumentWritten(
  { document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
  async (event) => {
    const collection = mailCollection.value();
    const password = smtpPassword.value();
    // ...
  }
);

৫. অভ্যন্তরীণ টাস্ক-কিউ কল স্থানান্তর করুন

কিছু এক্সটেনশন তাদের ফাংশন কোডের ভেতর থেকে Firebase Admin SDK ব্যবহার করে নিজস্ব টাস্ক কিউতে কাজ যুক্ত করে। এটি ডিসপ্যাচ করা টাস্ক গ্রহণ করার থেকে ভিন্ন (যা Upgrade functions এবং Convert lifecycle hooks সেকশনে আলোচনা করা হয়েছে)। এখানে আপনার কোড হলো প্রডিউসার , যা queue.enqueue(...) ফাংশনটিকে কল করে।

Admin SDK এর পূর্ববর্তী সংস্করণগুলিতে, একই এক্সটেনশনের মধ্যে থাকা কোনো টাস্ক কিউ ফাংশনকে টার্গেট করার জন্য এক্সটেনশনগুলিকে তাদের নিজস্ব এক্সটেনশন ইনস্ট্যান্স আইডি দ্বিতীয় প্যারামিটার হিসেবে পাস করতে হতো। firebase-admin 14.2.0 সংস্করণ থেকে, এটি আর প্রয়োজনীয় বা সুপারিশকৃত নয়। টাস্ক কিউ এপিআই এখন ডিফল্টরূপে একই কনটেক্সটের (যেমন, একটি এক্সটেনশন বা কিট) টাস্ক কিউগুলিকে টার্গেট করে। আপনার কোড থেকে এক্সটেনশন এবং স্বতন্ত্র ফাংশন উভয় ক্ষেত্রেই এই প্যারামিটারটি সরিয়ে ফেলা নিরাপদ এবং উৎসাহিত করা হয়। এই প্যারামিটারটি সরিয়ে ফেললে পোর্টেবিলিটি এবং ফরোয়ার্ড কম্প্যাটিবিলিটি নিশ্চিত হয়।

এনকিউ কল সম্পর্কিত বাকি সবকিছু — যেমন locations/<region>/functions/<name> রিসোর্স পাথ, টাস্ক পেলোড এবং আপনার রিট্রাই লজিক — একই থাকে।

ক্লাউড টাস্ক ব্যবহার করে ফাংশন এনকিউ করার বিষয়ে আরও বিস্তারিত জানতে Cloud Tasks মাধ্যমে ফাংশন এনকিউ করুন’ দেখুন।

পূর্বে। প্রথম প্রজন্মের এক্সটেনশন:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${config.location}/functions/syncBigQuery`,
  process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);

পরে। দ্বিতীয় প্রজন্মের সম্প্রসারণ:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);

আপনার এনকিউ কল যদি কোনো প্রিফিক্সযুক্ত কোডবেসকে টার্গেট করে, তাহলে আবিষ্কৃত ফাংশনের নামটিও প্রিফিক্সযুক্ত হয় (উদাহরণস্বরূপ, orders-syncBigQuery ); প্রতিস্থাপন ফাংশন কিট ইনস্ট্যান্সটি পর্যালোচনা ও ইনস্টল করুন এবং একটি ফাংশন কিট হিসাবে পরীক্ষা করুন দেখুন।

৬. প্রয়োজনীয় এপিআই (API) এবং আইএএম (IAM) রোলগুলো ঘোষণা করুন।

আপনার এক্সটেনশনের IAM এবং API সংক্রান্ত প্রয়োজনীয়তাগুলো extension.yaml থেকে সরিয়ে কোডে নিয়ে আসুন:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");

ডিক্লারেটিভ সিকিউরিটির মাধ্যমে, Firebase CLI কোডবেসের জন্য একটি ম্যানেজড রানটাইম সার্ভিস অ্যাকাউন্ট তৈরি বা আপডেট করে এবং এটিকে ঘোষিত সমস্ত রোলের সমষ্টি প্রদান করে। আপনার ব্যবহারকারীদের জন্য নথিভুক্ত করুন যে কোডবেসের সমস্ত ফাংশন সেই রোলগুলো ব্যবহার করে চলে, যদি না চূড়ান্ত API আরও সংকীর্ণ কোনো মডেল সমর্থন করে।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

পূর্বে, extension.yaml এ ঘোষিত; এক্সটেনশন রানটাইম API-টি সক্রিয় করেছিল এবং একটি পরিচালিত অ্যাকাউন্টে ভূমিকাগুলি প্রদান করেছিল:

apis:
  - apiName: bigquery.googleapis.com
roles:
  - role: bigquery.dataEditor
  - role: datastore.user
  - role: bigquery.user

পরে। কোডে requiresAPI এবং requiresRole দিয়ে ঘোষণা করা হয়েছে:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI(
  "bigquery.googleapis.com",
  "Needed to write changelog rows and views"
);
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");

আপনার এক্সটেনশন যদি ইভেন্টআর্ক ইভেন্ট প্রকাশ করে , তাহলে ইভেন্ট প্রকাশের জন্য আপনাকে উপযুক্ত প্রয়োজনীয় রোল এবং এপিআই-ও সেট করতে হবে। পূর্বে, আপনার extensions.yaml ফাইলে কোনো পরিবর্তন ছাড়াই এক্সটেনশনগুলোই এই কাজটি করত। আপনি আপনার কোডে শর্তসাপেক্ষে এটি করতে পারেন, যাতে কিটটি শুধুমাত্র একটি কাস্টম ইভেন্টআর্ক চ্যানেল ব্যবহার করার সময়ই এই অনুমতিগুলোর জন্য অনুরোধ করে।

if (!!process.env.EVENTARC_CHANNEL) {
  requiresRole("roles/eventarc.publisher");
  requiresAPI(
    "eventarcpublishing.googleapis.com",
    "Publishes the extension's custom events to its Eventarc channel."
  );
}

৭. লাইফসাইকেল হুক রূপান্তর করুন

যদি আপনার এক্সটেনশন getExtensions().runtime() কল করে (উদাহরণস্বরূপ, setProcessingState বা setFatalError ), তাহলে সেই কলগুলো মুছে ফেলুন, কারণ একটি স্বাভাবিকভাবে ডেপ্লয় করা ২য় প্রজন্মের ফাংশন থেকে কল করা হলে সেগুলো একটি এরর থ্রো করে। লাইফসাইকেল স্টেট এখন afterFirstDeploy এবং afterRedeploy দ্বারা চালিত হয়, যেখানে এই স্টেট ট্র্যাকিং ব্যবহৃত হয় না।

যখন কোনো ব্যবহারকারী একটি এক্সটেনশন ইনস্টল, আপডেট বা পুনঃকনফিগার করেন, তখন Firebase Extensions সেটআপ চালাতে পারে। আপনার npm প্যাকেজে, কোডের মাধ্যমে এর সমতুল্য লাইফসাইকেল অ্যাকশনগুলো ঘোষণা করুন।

এককালীন সেটআপের জন্য:

import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";

export const runInitialSetup = onTaskDispatched(async (request) => {
  await initializeResources(request.data);
});

afterFirstDeploy({
  task: {
    function: "runInitialSetup",
    body: {}
  }
});

কনফিগারেশন বা কোড আপডেটের জন্য:

import { afterRedeploy } from "firebase-functions/lifecycle";

afterRedeploy({
  task: {
    function: "runInitialSetup",
    body: { reconcile: true }
  }
});

আপনার লাইফসাইকেল অ্যাকশনগুলোকে আইডম্পোটেন্ট করুন। ডিসপ্যাচ বা এক্সিকিউশন ব্যর্থ হলে আপনার ব্যবহারকারীদের সেগুলোকে ম্যানুয়ালি পুনরায় চালানোর প্রয়োজন হতে পারে:

firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

পূর্বে extension.yaml এ lifecycleEvents , যা এক্সটেনশন রানটাইম দ্বারা চালিত হতো:

lifecycleEvents:
  onInstall:
    function: initBigQuerySync
    processingMessage: Configuring BigQuery Sync.
  onUpdate:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync
  onConfigure:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync

পরে। কোডে ঘোষিত; টাস্কটি প্রথম ডেপ্লয়ের সময় BigQuery প্রোভিশন করে:

import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";

afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });

প্রভিশনিং আইডম্পোটেন্ট, তাই পুনরায় চালালে ডেটাসেট, টেবিল এবং ভিউগুলো সামঞ্জস্যপূর্ণ হয়ে যায়। ব্যবহারকারীরা firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME ব্যবহার করে ম্যানুয়ালি পুনরায় চালাতে পারেন।

৮. আপনার ব্যবহারকারীদের জন্য ডকুমেন্ট সেটআপ

একটি প্যাকেজ README লিখুন যা ন্যূনতম নিম্নলিখিত বিষয়গুলো ব্যাখ্যা করে:

  • প্যাকেজটির জন্য প্রয়োজনীয় .env ভ্যালুগুলো।
  • প্যাকেজটির জন্য প্রয়োজনীয় গোপনীয় তথ্য এবং বিদ্যমান গোপনীয় মানগুলো কীভাবে স্থানান্তর করতে হয়।
  • প্যাকেজটি requiresRole(...) দিয়ে যে IAM রোলগুলো ঘোষণা করে।
  • প্যাকেজটি যে গুগল এপিআইগুলো সক্ষম করে বা যার জন্য প্রয়োজন হয়।
  • প্যাকেজটি যে লাইফসাইকেল হুকগুলো ঘোষণা করে, এবং কীভাবে সেগুলো ম্যানুয়ালি পুনরায় চালানো যায়।
  • বিলিং নোট।
  • মূল সম্প্রসারণের তুলনায় কী পরিবর্তন হয়েছে?
  • প্যাকেজটি কীভাবে তার ইনস্ট্যান্স আইডি (CLI দ্বারা সেট করা FIREBASE_KIT_INSTANCE_ID ) পায় এবং ইনস্ট্যান্সটির সমস্ত ফাংশন কীভাবে kit-<instanceId>- প্রিফিক্স সহ ডেপ্লয় করা হয়।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

প্যাকেজের README একটি সুনির্দিষ্ট 'কী পরিবর্তন হয়েছে' টেবিল থাকে:

উদ্বেগ সম্প্রসারণ হিসাবে @firebase-function-kits/firestore-bigquery-export হিসাবে
কনফিগারেশন এক্সটেনশন প্যারামিটার .env এর মাধ্যমে Cloud Functions প্যারামিটারসমূহ
আইএএম এক্সটেনশন দ্বারা মঞ্জুর করা হয়েছে requiresRole(...) , যা deploy-এর সময় প্রয়োগ করা হয়।
সরবরাহ এক্সটেনশন দ্বারা লাইফসাইকেল টাস্ক afterFirstDeploy / afterRedeploy টাস্ক
ফাংশনের নাম ext-<instanceId>-fsexportbigquery fsexportbigquery (ঐচ্ছিকভাবে উপসর্গযুক্ত)
ইনস্ট্যান্স আইডি এক্সটেনশন দ্বারা ইনজেক্ট করা EXT_INSTANCE_ID FIREBASE_KIT_INSTANCE_ID , যা firebase.json থেকে CLI দ্বারা সেট করা হয়।

৯. আপনার ২য় প্রজন্মের ফাংশনটি পরীক্ষা করুন

এখন আপনার কাছে একটি দ্বিতীয় প্রজন্মের ফাংশন থাকা উচিত, যা স্থাপন করার পর আপনার এক্সটেনশনের একটি নতুন ইনস্টলেশনের মতোই আচরণ করবে। পরবর্তী পদক্ষেপ হলো এই প্রক্রিয়ার মধ্যে দুর্ঘটনাবশত সৃষ্ট যেকোনো সমস্যা যাচাই করা এবং সমাধান করা।

ডিফল্ট রিজিয়ন বা সিপিইউ-এর মতো গ্লোবাল অপশন সেট করার জন্য setGlobalOptions কল করতে হলে, আপনাকে অবশ্যই তা শুধুমাত্র তখনই করতে হবে যখন আপনি আপনার কিটটিকে একটি স্বতন্ত্র ২য় প্রজন্মের ফাংশন হিসেবে ডেপ্লয় করছেন। যখন আপনার কিটটি একটি npm প্যাকেজ হিসেবে ইনস্টল করা হয়, তখন আপনার ব্যবহারকারীরা এই প্যারামিটারগুলো কনফিগার করার জন্য তাদের র‍্যাপিং কোডে setGlobalOptions কল করেন, এবং এমনটি দুবার ঘটলে তারা সতর্কবার্তা পাবেন। আপনি FIREBASE_KIT_INSTANCE_ID এনভায়রনমেন্ট ভেরিয়েবলটি পরীক্ষা করে এই কলটিকে সুরক্ষিত করতে পারেন:

import { setGlobalOptions } from "firebase-functions";

if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
  setGlobalOptions({
    region: "us-east1",
    maxInstances: 10,
  });
}

নিশ্চিত করুন যে আপনি firebase-tools >= 15.32.0 ব্যবহার করছেন, এবং এর আচরণ পরীক্ষা করার জন্য আপনার রূপান্তরিত ২য় প্রজন্মের ফাংশনটিকে উপযুক্ত রিসোর্সসহ একটি টেস্ট প্রজেক্টে ডেপ্লয় করুন। আপনার এক্সটেনশন পরীক্ষা করার জন্য যদি আগে থেকেই একটি টেস্ট প্রজেক্ট তৈরি করা থাকে, তাহলে নিম্নলিখিত কমান্ডটি চালান:

firebase deploy --only functions

এক্সটেনশনটির জন্য Firebase কনসোলে ইনস্টলেশন ফর্মটি আপনি যেভাবে পূরণ করেছিলেন, ঠিক সেভাবেই প্যারামিটার মানগুলির জন্য অনুরোধ করা উইজার্ডটি পূরণ করুন।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

আমরা Cloud Firestore থেকে BigQuery -তে সম্পূর্ণ সিঙ্ক প্রক্রিয়াটি যাচাই করি:

  1. Firebase কনসোলের Cloud Firestore পৃষ্ঠায়, আপনার সেট করা COLLECTION_PATH ( users ) কালেকশনটি তৈরি করুন, যদি সেটি আগে থেকে বিদ্যমান না থাকে।
  2. bigquery-mirror-test নামে একটি ডকুমেন্ট তৈরি করুন, যাতে যেকোনো ফিল্ডের যেকোনো মান থাকবে।
  3. Google Cloud কনসোলের BigQuery পেজে, raw changelog টেবিলটি কোয়েরি করুন। এতে ডকুমেন্ট তৈরির তথ্য সম্বলিত একটিমাত্র সারি থাকা উচিত:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
    
  4. সর্বশেষ ভিউটি কোয়েরি করুন, যা উপস্থিত একমাত্র ডকুমেন্টটির জন্য সর্বশেষ পরিবর্তন ইভেন্টটি ফেরত দেবে ( bigquery-mirror-test ):

    SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
    
  5. Cloud Firestore bigquery-mirror-test ডকুমেন্টটি ডিলিট করুন। এটি লেটেস্ট ভিউ থেকে অদৃশ্য হয়ে যাবে এবং র' চেঞ্জলগ টেবিলে একটি DELETE ইভেন্ট যুক্ত হবে।

    আপনি একটিমাত্র ডকুমেন্টের সম্পূর্ণ ইতিহাস নিম্নোক্ত উপায়ে দেখতে পারেন:

    SELECT *
       FROM `PROJECT_ID.analytics.users_raw_changelog`
       WHERE document_name = "bigquery-mirror-test"
       ORDER BY timestamp ASC
    

এক্সটেনশনটি পরীক্ষা করার ফলে প্রাপ্ত পার্থক্যসমূহ:

  • ট্রিগারটি fsexportbigquery হিসেবে ডিপ্লয় হয় (যখন এটি একটি সাধারণ ২য় প্রজন্মের ফাংশন হিসেবে ডিপ্লয় করা হয়, কোনো কিট হিসেবে নয়, তখন কোনো প্রিফিক্স থাকে না), ext-<instanceId>-fsexportbigquery নয়। Cloud Functions ড্যাশবোর্ড এবং লগ-এ এই নামটি খুঁজুন।
  • আপনার কোড এখন Firebase Local Emulator Suite সাধারণ ফাংশন হিসেবে চলে। আপনি .env.local ব্যবহার করে এমুলেটরে ব্যবহারের জন্য প্যারামিটারগুলোর মান নির্ধারণ করতে পারেন। এছাড়াও, Cloud Functions ইউনিট টেস্টিং অংশে বর্ণিত পদ্ধতি অনুযায়ী আপনি firebase-functions-test SDK ব্যবহার করে আপনার কোডের ইউনিট টেস্ট করতে পারেন।
  • প্রোভিশনিং এখন আর এক্সটেনশনস রানটাইম দ্বারা চালিত হয় না। ডিপ্লয়ের পর যদি চেঞ্জলগ টেবিলটি অনুপস্থিত থাকে, তাহলে সেটআপ টাস্কটি ম্যানুয়ালি পুনরায় চালান: firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME । টাস্কটি আইডম্পোটেন্ট, তাই এটি পুনরায় চালালে ডেটাসেট, টেবিল এবং ভিউগুলো রিকনসাইল হয়ে যায়।
  • প্যারামিটারের মানগুলো ইনস্টলেশন ফর্মের পরিবর্তে .env ফাইল থেকে আসে, তাই .env ফাইলের কাজ শেষ হয়ে গেলে firebase deploy পুনরায় চালালে তা আর ইন্টারেক্টিভ থাকে না।

১০. আপনার ফাংশন কিটটি npm-এ প্রকাশ করুন।

একবার আপনি এক্সটেনশন থেকে দ্বিতীয় প্রজন্মের ফাংশনে আপনার রূপান্তরটি যাচাই করে নিলে, নিম্নলিখিত নির্দেশিকাগুলির মধ্যে একটি ব্যবহার করে এন্ড-টু-এন্ড পরীক্ষার জন্য npm-এ একটি রিলিজ ক্যান্ডিডেট প্রকাশ করতে পারেন:

যখন আপনার কিটটি npm-এ প্রকাশিত হবে, তখন এটি firebase functions:kits:install মাধ্যমে ইনস্টল করা যাবে এবং আপনার এক্সটেনশনের আনুষ্ঠানিক প্রতিস্থাপন হিসেবে তালিকাভুক্ত হবে।

আমরা প্রথমে একটি রিলিজ ক্যান্ডিডেট প্রকাশ করার জন্য দৃঢ়ভাবে সুপারিশ করি। কিটগুলো প্যাকেজের নাম এবং ভার্সন অনুযায়ী ইনস্টল করা হয়, তাই একটি প্রি-রিলিজ আপনাকে রেজিস্ট্রি-র বিপরীতে আসল ইনস্টলেশন প্রক্রিয়াটি পরীক্ষা করার সুযোগ দেয়, এবং এর ফলে ডিফল্ট ' latest ট্যাগ দিয়ে ইনস্টল করা ব্যবহারকারীদের কাছে একটি অসম্পূর্ণ প্যাকেজ প্রকাশ করার ঝুঁকিও থাকে না।

প্রকাশ করার আগে:

  1. একটি প্যাকেজের নাম বেছে নিন। স্কোপড এবং আনস্কোপড উভয় প্রকার নামই কাজ করে (উপরের নির্দেশিকাগুলো দেখুন)। মনে রাখবেন যে স্কোপড প্যাকেজগুলো ডিফল্টরূপে প্রাইভেট থাকে, তাই --access public পাস করুন।
  2. বিল্ড করুন এবং দেখুন কী আউটপুট আসে। main এবং types আপনার কম্পাইল করা আউটপুটকে নির্দেশ করে (আমাদের উদাহরণে lib/ ), তাই প্রকাশিত tar ফাইলে এই ডিরেক্টরিটি অবশ্যই অন্তর্ভুক্ত করতে হবে। .npmignore অথবা একটি files allow-list ব্যবহার করুন, এবং npm pack --dry-run দিয়ে ফলাফল পরীক্ষা করুন। একটি prepublishOnly স্ক্রিপ্ট যা আপনার বিল্ড চালায়, তা পুরনো আউটপুট প্রকাশ হওয়া থেকে বিরত রাখে।

package.json এমন কিছু সংযোজন যা ডিফল্টরূপে পাবলিশিংকে নিরাপদ করে তোলে:

{
  "files": ["lib", "README.md", "CHANGELOG.md"],
  "publishConfig": { "access": "public", "tag": "next" },
  "scripts": {
    "build": "tsc -b",
    "prepublishOnly": "npm run build && npm test"
  }
}

"publishConfig": { "tag": "next" } ফিল্ডটি নিশ্চিত করে যে একটি সাধারণ npm publish কখনোই latest ওভাররাইট করবে না।

রিলিজ ক্যান্ডিডেট তৈরি করা:

উদাহরণস্বরূপ, স্থানীয়ভাবে ভার্সন 0.0.2-rc.3 থেকে 0.0.2-rc.4 এ বৃদ্ধি করতে (যদি package.json রিপোজিটরি রুটে থাকে, তবে এটি Git-এ কমিট এবং ট্যাগ যুক্ত করে):

npm version prerelease --preid rc

রিলিজ ক্যান্ডিডেট প্রকাশ করতে এবং npm-এ @your-org/your-kit@0.0.2-rc.4 রেজিস্টার করতে, next ট্যাগটি ব্যবহার করুন:

npm publish

npm ওয়েবসাইটে নতুন সংস্করণটি দেখাতে কয়েক মিনিট সময় লাগতে পারে; npm view সরাসরি রেজিস্ট্রি থেকে পড়ে:

npm view @your-org/your-kit versions dist-tags

এই নির্দেশিকার ধাপ ১১ এবং ১২ সম্পন্ন হয়ে গেলে, আপনি প্যাকেজটিকে একটি স্থিতিশীল সংস্করণে উন্নীত করতে পারবেন:

npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next

npm-shrinkwrap.json সম্পর্কে দ্রষ্টব্য: আমরা আপনার প্যাকেজের সাথে একটি npm-shrinkwrap.json ফাইল অন্তর্ভুক্ত করার জন্য দৃঢ়ভাবে সুপারিশ করি; যদি আপনি তা না করেন, তবে ইনস্টলের সময় CLI ব্যবহারকারীদের সতর্ক করে। এটি নিশ্চিত করে যে ব্যবহারকারীরা ঠিক সেইসব ডিপেন্ডেন্সি ব্যবহার করছেন যা আপনি পরীক্ষা করেছেন এবং সাপ্লাই চেইন অ্যাটাক থেকে রক্ষা করতে সাহায্য করে। তবে, একটি shrinkwrap আপনার ব্যবহারকারীদের প্রোজেক্টে হুবহু প্রয়োগ করা হয়, যার মধ্যে Cloud Functions বিল্ড ( npm ci ) অন্তর্ভুক্ত, যেখানে dev-only এন্ট্রিগুলো EBADPLATFORM ত্রুটির কারণে ব্যর্থ হতে পারে। প্রকাশিত shrinkwrap কপি থেকে আপনাকে "dev": true এন্ট্রি এবং devDependencies বাদ দিতে হতে পারে।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

কিটটির চতুর্থ রিলিজ ক্যান্ডিডেটের সময়কার package.json :

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.0.2-rc.4",
  "repository": {
    "type": "git",
    "url": "https://github.com/firebase/extensions.git",
    "directory": "kits/firestore-bigquery-export"
  },
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "engines": { "node": "22" },
  "scripts": { "build": "tsc -b" }
}

উল্লেখ্য যে, এক্ষেত্রে কিটটি একটি মোনোরেপোতে থাকে, তাই npm রেজিস্ট্রি লিঙ্কটি যাতে সঠিক ফোল্ডারে নির্দেশ করে, তার জন্য repository.directory অন্তর্ভুক্ত করা জরুরি। এর CHANGELOG.md ফাইলে আসন্ন রিলিজের জন্য নোট রয়েছে।

১১. ফাংশন কিট হিসেবে পরীক্ষা করুন

আপনার কিটটি প্রকাশ করার পর, আমরা npm ব্যবহার করে এটি পরীক্ষা করার পরামর্শ দিই।

নিশ্চিত করুন যে আপনি firebase-tools ভার্সন >= 15.32.0 ব্যবহার করছেন এবং কিটটি ইনস্টল করুন:

firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>

এটি npm থেকে আপনার প্যাকেজটি ডাউনলোড করে, আপনার কিটের জন্য একটি নতুন সোর্স ডিরেক্টরিতে এটি সেট আপ করে, এবং এক্সটেনশন ইনস্টলেশন প্রক্রিয়ার মতোই প্রথম ইনস্ট্যান্সটি কনফিগার করার পদ্ধতি ধাপে ধাপে দেখিয়ে দেয়। একবার আপনি আপনার প্যাকেজটি স্থানীয়ভাবে ইনস্টল এবং সেট আপ করে ফেললে, আপনার Google Cloud প্রোজেক্টে রিসোর্সগুলো তৈরি করতে একটি ডিপ্লয় চালান:

firebase deploy --only functions:<your-kit-instance-id>

ইনস্টলেশনের পরে, Firebase CLI ইনস্টলেশনের সময় আপনার বেছে নেওয়া সঠিক ইনস্ট্যান্স আইডি সহ একটি অনুরূপ ডিপ্লয় কমান্ড প্রিন্ট করে।

ধাপ ৯-এর নির্দেশাবলী ব্যবহার করে আপনার কিটটি আবার যাচাই করুন। আপনার দ্বিতীয় প্রজন্মের ফাংশনটি পরীক্ষা করুন । এখন যেহেতু আপনি কিট ব্যবহার করে ডেপ্লয় করছেন, আপনার ফাংশনগুলোর আগে kit-<instance-id>-<method-name> প্রিফিক্স যুক্ত হয়েছে এবং সেগুলোর নামকরণ করা হয়েছে। এর ফলে কিটগুলোর একাধিক ইনস্ট্যান্স থাকতে পারে, যার মাধ্যমে একটি প্রোজেক্টে একই ফাংশন একাধিকবার ডেপ্লয় করা যায় এবং প্রতিটির একটি অনন্য নাম থাকে।

১২. পরীক্ষা স্থানান্তর প্রতিস্থাপন

আপনি একটি কার্যকরী এক্সটেনশন ইনস্ট্যান্স সেট আপ করতে পারেন এবং তারপরে মাইগ্রেশন প্রতিস্থাপন হিসাবে আপনার ফাংশন কিটের পরীক্ষা শেষ করতে ইউজার মাইগ্রেশন গাইড ব্যবহার করতে পারেন (হয় firebase ext:migrate --package অথবা ফাংশন কিট CLI কমান্ড ব্যবহার করে)।

১৩. আপনার অফিসিয়াল এক্সটেনশন প্রতিস্থাপন সম্পর্কে ব্যবহারকারী এবং গুগলকে অবহিত করুন।

আপনার ফাংশন কিট প্রতিস্থাপনটি যখন একটি এনপিএম প্যাকেজ হিসেবে প্রস্তুত এবং ব্যবহারকারীদের জন্য উপলব্ধ হবে, তখন আপনার ব্যবহারকারী এবং গুগল উভয়কেই এই আনুষ্ঠানিক প্রতিস্থাপনটি সম্পর্কে অবহিত করুন। আপনার এক্সটেনশনটি যে গিটহাব রিপোজিটরিতে রয়েছে, সেখানকার README.md ফাইলটি নিম্নলিখিত তথ্য দিয়ে আপডেট করুন:

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="<your-extesion-id>" package="<your-npm-package-name>" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `<your-extension>` is deprecated. Migrate to the [<your-npm-package-name>](<link-to-your-npm-package>) package.

Google পরিচিত এক্সটেনশনের README ফাইলগুলো স্ক্যান করে <!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" --> মতো কমেন্ট খুঁজে বের করে এবং firebase-tools রিপোজিটরিতে replacements.json নামে সংরক্ষিত আমাদের অফিশিয়াল রিপ্লেসমেন্ট রেজিস্ট্রিটি পূরণ করতে এটি ব্যবহার করে। আপনার এক্সটেনশনের জন্য কোন README.md স্ক্যান করা হবে তা দেখতে আপনিও replacements.json দেখতে পারেন। রিপ্লেসমেন্টের অফিশিয়াল তালিকাটি প্রতি সপ্তাহে আপডেট করা হয়।

কার্যকারী উদাহরণ: Cloud Firestore থেকে BigQuery ডেটা স্ট্রিম করা

firestore-bigquery-export এক্সটেনশনের README.md তে রয়েছে:

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `firebase/firestore-bigquery-export` is deprecated. Please migrate to the [`@firebase-function-kits/firestore-bigquery-export`](https://www.npmjs.com/package/@firebase-function-kits/firestore-bigquery-export) package.