ফায়ারবেস এমুলেটর স্যুটের সাথে স্থানীয় উন্নয়ন

১. শুরু করার আগে

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

পূর্বশর্ত

  • ভিজ্যুয়াল স্টুডিও কোড, অ্যাটম, অথবা সাব্লাইম টেক্সটের মতো একটি সাধারণ সম্পাদক
  • Node.js 10.0.0 বা তার বেশি (Node.js ইনস্টল করতে, nvm ব্যবহার করুন , আপনার সংস্করণ পরীক্ষা করতে, node --version চালান)
  • জাভা ৭ বা তার উচ্চতর (জাভা ইনস্টল করতে এই নির্দেশাবলী ব্যবহার করুন , আপনার সংস্করণ পরীক্ষা করতে, java -version চালান)

তুমি কি করবে

এই কোডল্যাবে, আপনি একটি সহজ অনলাইন শপিং অ্যাপ চালাবেন এবং ডিবাগ করবেন যা একাধিক ফায়ারবেস পরিষেবা দ্বারা চালিত:

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

স্থানীয় উন্নয়ন সক্ষম করতে আপনি অ্যাপটিকে এমুলেটর স্যুটের সাথে সংযুক্ত করবেন।

2589e2f95b74fa88.png সম্পর্কে

আপনি আরও শিখবেন কিভাবে:

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

2. সেট আপ করুন

সোর্স কোডটি পান

এই কোডল্যাবে, আপনি দ্য ফায়ার স্টোর নমুনার একটি সংস্করণ দিয়ে শুরু করবেন যা প্রায় সম্পূর্ণ, তাই আপনাকে প্রথমে যা করতে হবে তা হল সোর্স কোডটি ক্লোন করা:

$ git clone https://github.com/firebase/emulators-codelab.git

তারপর কোডল্যাব ডিরেক্টরিতে যান, যেখানে আপনি এই কোডল্যাবের বাকি অংশের জন্য কাজ করবেন:

$ cd emulators-codelab/codelab-initial-state

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

# Move into the functions directory
$ cd functions

# Install dependencies
$ npm install

# Move back into the previous directory
$ cd ../

ফায়ারবেস সিএলআই পান

এমুলেটর স্যুটটি ফায়ারবেস সিএলআই (কমান্ড-লাইন ইন্টারফেস) এর অংশ যা নিম্নলিখিত কমান্ডের সাহায্যে আপনার মেশিনে ইনস্টল করা যেতে পারে:

$ npm install -g firebase-tools

এরপর, নিশ্চিত করুন যে আপনার কাছে CLI এর সর্বশেষ সংস্করণ আছে। এই কোডল্যাবটি 9.0.0 বা তার উচ্চতর সংস্করণের সাথে কাজ করবে তবে পরবর্তী সংস্করণগুলিতে আরও বাগ সংশোধন অন্তর্ভুক্ত রয়েছে।

$ firebase --version
9.6.0

আপনার Firebase প্রকল্পের সাথে সংযোগ করুন

একটি ফায়ারবেস প্রকল্প তৈরি করুন

  1. আপনার গুগল অ্যাকাউন্ট ব্যবহার করে ফায়ারবেস কনসোলে সাইন ইন করুন।
  2. একটি নতুন প্রকল্প তৈরি করতে বোতামটি ক্লিক করুন, এবং তারপর একটি প্রকল্পের নাম লিখুন (উদাহরণস্বরূপ, Emulators Codelab )।
  3. চালিয়ে যান ক্লিক করুন।
  4. যদি অনুরোধ করা হয়, তাহলে Firebase শর্তাবলী পর্যালোচনা করুন এবং গ্রহণ করুন, এবং তারপর Continue এ ক্লিক করুন।
  5. (ঐচ্ছিক) Firebase কনসোলে ("Gemini in Firebase" নামে পরিচিত) AI সহায়তা সক্ষম করুন।
  6. এই কোডল্যাবের জন্য, আপনার গুগল অ্যানালিটিক্সের প্রয়োজন নেই , তাই গুগল অ্যানালিটিক্স বিকল্পটি টগল করে বন্ধ করে দিন
  7. Create project এ ক্লিক করুন, আপনার province করার জন্য অপেক্ষা করুন, এবং তারপর Continue এ ক্লিক করুন।

আপনার কোডটি আপনার Firebase প্রকল্পের সাথে সংযুক্ত করুন

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

$ firebase login

এরপর একটি প্রজেক্ট উপনাম তৈরি করতে নিম্নলিখিত কমান্ডটি চালান। $YOUR_PROJECT_ID আপনার Firebase প্রজেক্টের আইডি দিয়ে প্রতিস্থাপন করুন।

$ firebase use $YOUR_PROJECT_ID

এখন আপনি অ্যাপটি চালানোর জন্য প্রস্তুত!

৩. এমুলেটরগুলি চালান

এই বিভাগে, আপনি স্থানীয়ভাবে অ্যাপটি চালাবেন। এর অর্থ হল এমুলেটর স্যুটটি বুট করার সময় এসেছে।

এমুলেটরগুলি শুরু করুন

কোডল্যাব সোর্স ডিরেক্টরির ভেতর থেকে, এমুলেটরগুলি শুরু করতে নিম্নলিখিত কমান্ডটি চালান:

$ firebase emulators:start --import=./seed

তুমি এরকম কিছু আউটপুট দেখতে পাবে:

$ firebase emulators:start --import=./seed
i  emulators: Starting emulators: auth, functions, firestore, hosting
⚠  functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: database, pubsub
i  firestore: Importing data from /Users/samstern/Projects/emulators-codelab/codelab-initial-state/seed/firestore_export/firestore_export.overall_export_metadata
i  firestore: Firestore Emulator logging to firestore-debug.log
i  hosting: Serving hosting files from: public
✔  hosting: Local server: http://127.0.0.1:5000
i  ui: Emulator UI logging to ui-debug.log
i  functions: Watching "/Users/samstern/Projects/emulators-codelab/codelab-initial-state/functions" for Cloud Functions...
✔  functions[calculateCart]: firestore function initialized.

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions      │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Hosting        │ 127.0.0.1:5000 │ n/a                             │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at 127.0.0.1:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

একবার আপনি " All emulators started " বার্তাটি দেখতে পেলে, অ্যাপটি ব্যবহারের জন্য প্রস্তুত।

ওয়েব অ্যাপটিকে এমুলেটরের সাথে সংযুক্ত করুন

লগের টেবিলের উপর ভিত্তি করে আমরা দেখতে পাচ্ছি যে ক্লাউড ফায়ারস্টোর এমুলেটর 8080 পোর্টে শুনছে এবং প্রমাণীকরণ এমুলেটর 9099 পোর্টে শুনছে।

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions      │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Hosting        │ 127.0.0.1:5000 │ n/a                             │
└────────────────┴────────────────┴─────────────────────────────────┘

আপনার ফ্রন্টএন্ড কোডটি প্রোডাকশনের পরিবর্তে এমুলেটরের সাথে সংযুক্ত করা যাক। public/js/homepage.js ফাইলটি খুলুন এবং onDocumentReady ফাংশনটি খুঁজুন। আমরা দেখতে পাচ্ছি যে কোডটি স্ট্যান্ডার্ড Firestore এবং Auth ইনস্ট্যান্সগুলিতে অ্যাক্সেস করে:

পাবলিক/জেএস/হোমপেজ.জেএস

  const auth = firebaseApp.auth();
  const db = firebaseApp.firestore();

স্থানীয় এমুলেটরগুলিকে নির্দেশ করার জন্য db এবং auth অবজেক্টগুলি আপডেট করা যাক:

পাবলিক/জেএস/হোমপেজ.জেএস

  const auth = firebaseApp.auth();
  const db = firebaseApp.firestore();

  // ADD THESE LINES
  if (location.hostname === "127.0.0.1") {
    console.log("127.0.0.1 detected!");
    auth.useEmulator("http://127.0.0.1:9099");
    db.useEmulator("127.0.0.1", 8080);
  }

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

এমুলেটরইউআই খুলুন।

আপনার ওয়েব ব্রাউজারে, http://127.0.0.1:4000/ এ যান। আপনি এমুলেটর স্যুট UI দেখতে পাবেন।

এমুলেটর UI হোম স্ক্রিন

ফায়ারস্টোর এমুলেটরের UI দেখতে ক্লিক করুন। --import পতাকা ব্যবহার করে ডেটা আমদানি করার কারণে items সংগ্রহে ইতিমধ্যেই ডেটা রয়েছে।

4ef88d0148405d36.png সম্পর্কে

৪. অ্যাপটি চালান

অ্যাপটি খুলুন

আপনার ওয়েব ব্রাউজারে, http://127.0.0.1:5000 এ যান এবং আপনি আপনার মেশিনে স্থানীয়ভাবে চলমান The Fire Store দেখতে পাবেন!

939f87946bac2ee4.png সম্পর্কে

অ্যাপটি ব্যবহার করুন

হোমপেজে একটি আইটেম নির্বাচন করুন এবং Add to Cart এ ক্লিক করুন। দুর্ভাগ্যবশত, আপনি নিম্নলিখিত ত্রুটির সম্মুখীন হবেন:

a11bd59933a8e885.png সম্পর্কে

চলো বাগটা ঠিক করি! যেহেতু এমুলেটরে সবকিছু চলছে, তাই আমরা পরীক্ষা-নিরীক্ষা করতে পারি এবং প্রকৃত ডেটা প্রভাবিত করার বিষয়ে চিন্তা করতে পারি না।

৫. অ্যাপটি ডিবাগ করুন

বাগটি খুঁজুন

ঠিক আছে, Chrome ডেভেলপার কনসোলে দেখা যাক। কনসোলে ত্রুটি দেখতে Control+Shift+J (Windows, Linux, Chrome OS) অথবা Command+Option+J (Mac) টিপুন:

74c45df55291dab1.png সম্পর্কে

মনে হচ্ছে addToCart পদ্ধতিতে কিছু ত্রুটি ছিল, আসুন একবার দেখে নেওয়া যাক। আমরা কোথায় uid নামক কিছু অ্যাক্সেস করার চেষ্টা করি এবং কেন এটি null হবে? এই মুহূর্তে public/js/homepage.js পদ্ধতিতে পদ্ধতিটি এরকম দেখাচ্ছে:

পাবলিক/জেএস/হোমপেজ.জেএস

  addToCart(id, itemData) {
    console.log("addToCart", id, JSON.stringify(itemData));
    return this.db
      .collection("carts")
      .doc(this.auth.currentUser.uid)
      .collection("items")
      .doc(id)
      .set(itemData);
  }

আহা! আমরা অ্যাপে সাইন ইন করিনি। Firebase Authentication ডক্স অনুসারে, যখন আমরা সাইন ইন না থাকি, তখন auth.currentUser null থাকে। এর জন্য একটি চেক যোগ করা যাক:

পাবলিক/জেএস/হোমপেজ.জেএস

  addToCart(id, itemData) {
    // ADD THESE LINES
    if (this.auth.currentUser === null) {
      this.showError("You must be signed in!");
      return;
    }

    // ...
  }

অ্যাপটি পরীক্ষা করুন

এখন, পৃষ্ঠাটি রিফ্রেশ করুন এবং তারপর Add to Cart এ ক্লিক করুন। এবার আপনার আরও ভালো একটি ত্রুটি পাওয়া উচিত:

c65f6c05588133f7.png সম্পর্কে

কিন্তু যদি আপনি উপরের টুলবারে "Sign In" এ ক্লিক করেন এবং তারপর আবার "Add to Cart" এ ক্লিক করেন, তাহলে আপনি দেখতে পাবেন যে কার্টটি আপডেট করা হয়েছে।

তবে, মনে হচ্ছে সংখ্যাগুলি মোটেও সঠিক নয়:

239f26f02f959eef.png সম্পর্কে

চিন্তা করবেন না, আমরা শীঘ্রই সেই বাগটি ঠিক করব। প্রথমে, আপনার কার্টে একটি আইটেম যোগ করার সময় আসলে কী ঘটেছিল তা গভীরভাবে জেনে নেওয়া যাক।

৬. স্থানীয় ফাংশন ট্রিগার

"কার্টে যোগ করুন" এ ক্লিক করলে একাধিক এমুলেটর জড়িত ইভেন্টের একটি শৃঙ্খল শুরু হয়। Firebase CLI লগে, আপনার কার্টে একটি আইটেম যোগ করার পরে আপনি নিম্নলিখিত বার্তাগুলির মতো কিছু দেখতে পাবেন:

i  functions: Beginning execution of "calculateCart"
i  functions: Finished "calculateCart" in ~1s

এই লগগুলি এবং আপনার পর্যবেক্ষণ করা UI আপডেট তৈরি করার জন্য চারটি গুরুত্বপূর্ণ ঘটনা ঘটেছে:

68c9323f2ad10f7a.png সম্পর্কে

১) ফায়ারস্টোর রাইট - ক্লায়েন্ট

Firestore সংগ্রহে /carts/{cartId}/items/{itemId}/ একটি নতুন ডকুমেন্ট যোগ করা হয়েছে। আপনি public/js/homepage.js এর ভিতরে addToCart ফাংশনে এই কোডটি দেখতে পাবেন:

পাবলিক/জেএস/হোমপেজ.জেএস

  addToCart(id, itemData) {
    // ...
    console.log("addToCart", id, JSON.stringify(itemData));
    return this.db
      .collection("carts")
      .doc(this.auth.currentUser.uid)
      .collection("items")
      .doc(id)
      .set(itemData);
  }

2) ক্লাউড ফাংশন ট্রিগার করা হয়েছে

ক্লাউড ফাংশন calculateCart কার্ট আইটেমের সাথে ঘটে যাওয়া যেকোনো লেখার ইভেন্ট (তৈরি, আপডেট বা মুছে ফেলা) onWrite ট্রিগার ব্যবহার করে শোনে, যা আপনি functions/index.js এ দেখতে পাবেন:

ফাংশন/ইন্ডেক্স.জেএস

exports.calculateCart = functions.firestore
    .document("carts/{cartId}/items/{itemId}")
    .onWrite(async (change, context) => {
      try {
        let totalPrice = 125.98;
        let itemCount = 8;

        const cartRef = db.collection("carts").doc(context.params.cartId);

        await cartRef.update({
          totalPrice,
          itemCount
        });
      } catch(err) {
      }
    }
);

৩) ফায়ারস্টোর লিখুন - অ্যাডমিন

calculateCart ফাংশনটি কার্টের সমস্ত আইটেম পড়ে এবং মোট পরিমাণ এবং মূল্য যোগ করে, তারপর এটি "cart" ডকুমেন্টটিকে নতুন মোট সংখ্যা দিয়ে আপডেট করে (উপরে cartRef.update(...) দেখুন)।

৪) ফায়ারস্টোর রিড - ক্লায়েন্ট

কার্টে পরিবর্তন সম্পর্কে আপডেট পেতে ওয়েব ফ্রন্টএন্ডটি সাবস্ক্রাইব করা হয়েছে। ক্লাউড ফাংশন নতুন টোটাল লেখার পরে এবং UI আপডেট করার পরে এটি একটি রিয়েল-টাইম আপডেট পায়, যেমনটি আপনি public/js/homepage.js তে দেখতে পারেন:

পাবলিক/জেএস/হোমপেজ.জেএস

this.cartUnsub = cartRef.onSnapshot(cart => {
   // The cart document was changed, update the UI
   // ...
});

সংক্ষিপ্তসার

দারুন কাজ! আপনি একটি সম্পূর্ণ স্থানীয় অ্যাপ সেট আপ করেছেন যা সম্পূর্ণ স্থানীয় পরীক্ষার জন্য তিনটি ভিন্ন Firebase এমুলেটর ব্যবহার করে।

অনুসরণ

কিন্তু অপেক্ষা করুন, আরও অনেক কিছু আছে! পরবর্তী বিভাগে আপনি শিখবেন:

  • ফায়ারবেস এমুলেটর ব্যবহার করে ইউনিট পরীক্ষা কীভাবে লিখবেন।
  • আপনার নিরাপত্তা নিয়মগুলি ডিবাগ করার জন্য ফায়ারবেস এমুলেটরগুলি কীভাবে ব্যবহার করবেন।

৭. আপনার অ্যাপের জন্য তৈরি নিরাপত্তা নিয়ম তৈরি করুন

আমাদের ওয়েব অ্যাপ ডেটা পড়ে এবং লেখে কিন্তু এখনও পর্যন্ত আমরা নিরাপত্তা নিয়ে মোটেও চিন্তিত নই। ক্লাউড ফায়ারস্টোর "নিরাপত্তা নিয়ম" নামক একটি সিস্টেম ব্যবহার করে কার ডেটা পড়তে এবং লেখার অ্যাক্সেস আছে তা ঘোষণা করে। এমুলেটর স্যুট এই নিয়মগুলির প্রোটোটাইপ তৈরির একটি দুর্দান্ত উপায়।

এডিটরে, emulators-codelab/codelab-initial-state/firestore.rules ফাইলটি খুলুন। আপনি দেখতে পাবেন যে আমাদের নিয়মগুলিতে তিনটি প্রধান বিভাগ রয়েছে:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // User's cart metadata
    match /carts/{cartID} {
      // TODO: Change these! Anyone can read or write.
      allow read, write: if true;
    }

    // Items inside the user's cart
    match /carts/{cartID}/items/{itemID} {
      // TODO: Change these! Anyone can read or write.
      allow read, write: if true;
    }

    // All items available in the store. Users can read
    // items but never write them.
    match /items/{itemID} {
      allow read: if true;
    }
  }
}

এখন যে কেউ আমাদের ডাটাবেসে তথ্য পড়তে এবং লিখতে পারে! আমরা নিশ্চিত করতে চাই যে কেবলমাত্র বৈধ ক্রিয়াকলাপগুলিই সম্পন্ন হয় এবং আমরা কোনও সংবেদনশীল তথ্য ফাঁস না করি।

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

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // User's cart metadata
    match /carts/{cartID} {
      // UPDATE THIS LINE
      allow read, write: if false;
    }

    // Items inside the user's cart
    match /carts/{cartID}/items/{itemID} {
      // UPDATE THIS LINE
      allow read, write: if false;
    }

    // All items available in the store. Users can read
    // items but never write them.
    match /items/{itemID} {
      allow read: if true;
    }
  }
}

৮. এমুলেটর এবং পরীক্ষা চালান

এমুলেটরগুলি শুরু করুন

কমান্ড লাইনে, নিশ্চিত করুন যে আপনি emulators-codelab/codelab-initial-state/ এ আছেন। আপনার পূর্ববর্তী ধাপগুলি থেকে এখনও এমুলেটরগুলি চালু থাকতে পারে। যদি না হয়, তাহলে আবার এমুলেটরগুলি শুরু করুন:

$ firebase emulators:start --import=./seed

একবার এমুলেটরগুলি চালু হয়ে গেলে, আপনি স্থানীয়ভাবে তাদের বিরুদ্ধে পরীক্ষা চালাতে পারেন।

পরীক্ষাগুলি চালান

emulators-codelab/codelab-initial-state/ ডিরেক্টরি থেকে একটি নতুন টার্মিনাল ট্যাবে কমান্ড লাইনে

প্রথমে ফাংশন ডিরেক্টরিতে যান (কোডল্যাবের বাকি অংশের জন্য আমরা এখানেই থাকব):

$ cd functions

এখন ফাংশন ডিরেক্টরিতে মোচা পরীক্ষা চালান এবং আউটপুটের শীর্ষে স্ক্রোল করুন:

# Run the tests
$ npm test

> functions@ test .../emulators-codelab/codelab-initial-state/functions
> mocha

  shopping carts
    1) can be created and updated by the cart owner
    2) can be read only by the cart owner

  shopping cart items
    3) can be read only by the cart owner
    4) can be added only by the cart owner

  adding an item to the cart recalculates the cart total. 
    - should sum the cost of their items


  0 passing (364ms)
  1 pending
  4 failing

এই মুহূর্তে আমাদের চারটি ব্যর্থতা আছে। নিয়ম ফাইল তৈরি করার সাথে সাথে, আপনি আরও পরীক্ষায় উত্তীর্ণ হতে দেখে অগ্রগতি পরিমাপ করতে পারবেন।

৯. নিরাপদ কার্ট অ্যাক্সেস

প্রথম দুটি ব্যর্থতা হল "শপিং কার্ট" পরীক্ষা যা পরীক্ষা করে যে:

  • ব্যবহারকারীরা কেবল তাদের নিজস্ব কার্ট তৈরি এবং আপডেট করতে পারবেন।
  • ব্যবহারকারীরা কেবল তাদের নিজস্ব কার্ট পড়তে পারবেন

ফাংশন/test.js

  it('can be created and updated by the cart owner', async () => {
    // Alice can create her own cart
    await firebase.assertSucceeds(aliceDb.doc("carts/alicesCart").set({
      ownerUID: "alice",
      total: 0
    }));

    // Bob can't create Alice's cart
    await firebase.assertFails(bobDb.doc("carts/alicesCart").set({
      ownerUID: "alice",
      total: 0
    }));

    // Alice can update her own cart with a new total
    await firebase.assertSucceeds(aliceDb.doc("carts/alicesCart").update({
      total: 1
    }));

    // Bob can't update Alice's cart with a new total
    await firebase.assertFails(bobDb.doc("carts/alicesCart").update({
      total: 1
    }));
  });

  it("can be read only by the cart owner", async () => {
    // Setup: Create Alice's cart as admin
    await admin.doc("carts/alicesCart").set({
      ownerUID: "alice",
      total: 0
    });

    // Alice can read her own cart
    await firebase.assertSucceeds(aliceDb.doc("carts/alicesCart").get());

    // Bob can't read Alice's cart
    await firebase.assertFails(bobDb.doc("carts/alicesCart").get());
  });

এই পরীক্ষাগুলো পাস করা যাক। এডিটরে, নিরাপত্তা নিয়ম ফাইল, firestore.rules খুলুন এবং match /carts/{cartID} এর মধ্যে থাকা বিবৃতিগুলি আপডেট করুন:

ফায়ারস্টোর.নিয়ম

rules_version = '2';
service cloud.firestore {
    // UPDATE THESE LINES
    match /carts/{cartID} {
      allow create: if request.auth.uid == request.resource.data.ownerUID;
      allow read, update, delete: if request.auth.uid == resource.data.ownerUID;
    }

    // ...
  }
}

এই নিয়মগুলি এখন কেবল কার্ট মালিককেই পঠন এবং লেখার অ্যাক্সেস দেয়।

ইনকামিং ডেটা এবং ব্যবহারকারীর প্রমাণীকরণ যাচাই করার জন্য, আমরা প্রতিটি নিয়মের প্রেক্ষাপটে উপলব্ধ দুটি বস্তু ব্যবহার করি:

  • request বস্তুটিতে যে অপারেশনটি করার চেষ্টা করা হচ্ছে সে সম্পর্কে ডেটা এবং মেটাডেটা রয়েছে।
  • যদি কোন Firebase প্রকল্প Firebase Authentication ব্যবহার করে, তাহলে request.auth অবজেক্টটি সেই ব্যবহারকারীকে বর্ণনা করে যিনি অনুরোধ করছেন।

১০. টেস্ট কার্ট অ্যাক্সেস

firestore.rules সেভ হলেই এমুলেটর স্যুট স্বয়ংক্রিয়ভাবে নিয়ম আপডেট করে। এমুলেটরটি যে নিয়ম আপডেট করেছে তা নিশ্চিত করতে আপনি এমুলেটরটি রানিং ট্যাবে " Rules updated :" বার্তাটি দেখে নিশ্চিত করতে পারেন।

5680da418b420226.png সম্পর্কে

পরীক্ষাগুলি পুনরায় চালান, এবং পরীক্ষা করুন যে প্রথম দুটি পরীক্ষা এখন পাস করেছে:

$ npm test

> functions@ test .../emulators-codelab/codelab-initial-state/functions
> mocha

  shopping carts
    ✓ can be created and updated by the cart owner (195ms)
    ✓ can be read only by the cart owner (136ms)

  shopping cart items
    1) can be read only by the cart owner
    2) can be added only by the cart owner

  adding an item to the cart recalculates the cart total. 
    - should sum the cost of their items

  2 passing (482ms)
  1 pending
  2 failing

ভালো কাজ! এখন তুমি শপিং কার্টে প্রবেশাধিকার নিশ্চিত করেছো। চলুন পরবর্তী ব্যর্থ পরীক্ষায় যাই।

১১. UI-তে "Add to Cart" ফ্লোটি পরীক্ষা করুন।

বর্তমানে, যদিও কার্টের মালিকরা তাদের কার্টে লেখা এবং পড়া করতে পারেন, তারা তাদের কার্টের পৃথক আইটেমগুলি পড়তে বা লিখতে পারেন না। কারণ মালিকদের কার্ট ডকুমেন্টে অ্যাক্সেস থাকলেও, কার্টের আইটেম সাবকালেকশনে তাদের অ্যাক্সেস নেই।

এটি ব্যবহারকারীদের জন্য একটি ভাঙা অবস্থা।

http://127.0.0.1:5000, তে চলমান ওয়েব UI-তে ফিরে যান এবং আপনার কার্টে কিছু যোগ করার চেষ্টা করুন। আপনি একটি Permission Denied ত্রুটি পাবেন, যা ডিবাগ কনসোল থেকে দৃশ্যমান হবে, কারণ আমরা এখনও ব্যবহারকারীদের items সাবকালেকশনে তৈরি নথিতে অ্যাক্সেস দেইনি।

১২. কার্ট আইটেম অ্যাক্সেসের অনুমতি দিন

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

  it("can be read only by the cart owner", async () => {
    // Alice can read items in her own cart
    await firebase.assertSucceeds(aliceDb.doc("carts/alicesCart/items/milk").get());

    // Bob can't read items in alice's cart
    await firebase.assertFails(bobDb.doc("carts/alicesCart/items/milk").get())
  });

  it("can be added only by the cart owner",  async () => {
    // Alice can add an item to her own cart
    await firebase.assertSucceeds(aliceDb.doc("carts/alicesCart/items/lemon").set({
      name: "lemon",
      price: 0.99
    }));

    // Bob can't add an item to alice's cart
    await firebase.assertFails(bobDb.doc("carts/alicesCart/items/lemon").set({
      name: "lemon",
      price: 0.99
    }));
  });

তাই আমরা এমন একটি নিয়ম লিখতে পারি যা বর্তমান ব্যবহারকারীর কার্ট ডকুমেন্টে ownerUID এর মতো একই UID থাকলে অ্যাক্সেসের অনুমতি দেয়। যেহেতু create, update, delete এর জন্য আলাদা নিয়ম নির্দিষ্ট করার প্রয়োজন নেই, তাই আপনি একটি write নিয়ম ব্যবহার করতে পারেন, যা ডেটা পরিবর্তনকারী সমস্ত অনুরোধের ক্ষেত্রে প্রযোজ্য।

আইটেম সাবকালেকশনের ডকুমেন্টের জন্য নিয়মটি আপডেট করুন। শর্তাধীনে get অর্থ হল ফায়ারস্টোর থেকে একটি মান পড়া - এই ক্ষেত্রে, কার্ট ডকুমেন্টের ownerUID

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ...

    // UPDATE THESE LINES
    match /carts/{cartID}/items/{itemID} {
      allow read, write: if get(/databases/$(database)/documents/carts/$(cartID)).data.ownerUID == request.auth.uid;
    }

    // ...
  }
}

১৩. টেস্ট কার্ট আইটেম অ্যাক্সেস

এখন আমরা পরীক্ষাটি পুনরায় চালাতে পারি। আউটপুটের শীর্ষে স্ক্রোল করুন এবং আরও পরীক্ষা পাস হয়েছে কিনা তা পরীক্ষা করুন:

$ npm test

> functions@ test .../emulators-codelab/codelab-initial-state/functions
> mocha

  shopping carts
    ✓ can be created and updated by the cart owner (195ms)
    ✓ can be read only by the cart owner (136ms)

  shopping cart items
    ✓ can be read only by the cart owner (111ms)
    ✓ can be added only by the cart owner


  adding an item to the cart recalculates the cart total. 
    - should sum the cost of their items


  4 passing (401ms)
  1 pending

দারুন! এখন আমাদের সব পরীক্ষা শেষ। আমাদের একটি পরীক্ষা বাকি আছে, কিন্তু আমরা কয়েক ধাপে সেই পরীক্ষায় পৌঁছাবো।

১৪. "কার্টে যোগ করুন" প্রবাহটি আবার পরীক্ষা করুন।

ওয়েব ফ্রন্ট এন্ডে ফিরে যান ( http://127.0.0.1:5000 ) এবং কার্টে একটি আইটেম যোগ করুন। আমাদের পরীক্ষা এবং নিয়মগুলি ক্লায়েন্টের প্রয়োজনীয় কার্যকারিতার সাথে মেলে কিনা তা নিশ্চিত করার জন্য এটি একটি গুরুত্বপূর্ণ পদক্ষেপ। (মনে রাখবেন যে শেষবার যখন আমরা চেষ্টা করেছিলাম তখন UI ব্যবহারকারীরা তাদের কার্টে আইটেম যোগ করতে পারেননি!)

69ad26cee520bf24.png সম্পর্কে

firestore.rules সংরক্ষণ করা হলে ক্লায়েন্ট স্বয়ংক্রিয়ভাবে নিয়মগুলি পুনরায় লোড করে। তাই, কার্টে কিছু যোগ করার চেষ্টা করুন।

সংক্ষিপ্তসার

দারুন কাজ! আপনি আপনার অ্যাপের নিরাপত্তা উন্নত করেছেন, এটি উৎপাদনের জন্য প্রস্তুত করার জন্য একটি অপরিহার্য পদক্ষেপ! যদি এটি একটি উৎপাদন অ্যাপ হত, তাহলে আমরা আমাদের ক্রমাগত ইন্টিগ্রেশন পাইপলাইনে এই পরীক্ষাগুলি যুক্ত করতে পারতাম। এটি আমাদের আত্মবিশ্বাস দেবে যে আমাদের শপিং কার্ট ডেটাতে এই অ্যাক্সেস নিয়ন্ত্রণ থাকবে, এমনকি অন্যরা নিয়ম পরিবর্তন করলেও।

অনুসরণ

কিন্তু অপেক্ষা করুন, আরও অনেক কিছু আছে!

যদি তুমি এগিয়ে যাও, তাহলে তুমি শিখবে:

  • ফায়ারস্টোর ইভেন্ট দ্বারা ট্রিগার হওয়া একটি ফাংশন কীভাবে লিখবেন
  • একাধিক এমুলেটর জুড়ে কাজ করে এমন পরীক্ষা কীভাবে তৈরি করবেন

১৫. ক্লাউড ফাংশন পরীক্ষা সেট আপ করুন

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

এমুলেটর স্যুট ক্লাউড ফাংশন পরীক্ষা করা এত সহজ করে তোলে, এমনকি ক্লাউড ফায়ারস্টোর এবং অন্যান্য পরিষেবা ব্যবহারকারী ফাংশনগুলিও।

এডিটরে, emulators-codelab/codelab-initial-state/functions/test.js ফাইলটি খুলুন এবং ফাইলের শেষ পরীক্ষায় স্ক্রোল করুন। এখন, এটি মুলতুবি হিসাবে চিহ্নিত করা হয়েছে:

//  REMOVE .skip FROM THIS LINE
describe.skip("adding an item to the cart recalculates the cart total. ", () => {
  // ...

  it("should sum the cost of their items", async () => {
    ...
  });
});

পরীক্ষাটি সক্রিয় করতে, .skip সরিয়ে ফেলুন, যাতে এটি এরকম দেখায়:

describe("adding an item to the cart recalculates the cart total. ", () => {
  // ...

  it("should sum the cost of their items", async () => {
    ...
  });
});

এরপর, ফাইলের উপরে REAL_FIREBASE_PROJECT_ID ভেরিয়েবলটি খুঁজুন এবং এটিকে আপনার আসল Firebase Project ID তে পরিবর্তন করুন।

// CHANGE THIS LINE
const REAL_FIREBASE_PROJECT_ID = "changeme";

যদি আপনি আপনার প্রজেক্ট আইডি ভুলে যান, তাহলে আপনি ফায়ারবেস কনসোলের প্রজেক্ট সেটিংসে আপনার ফায়ারবেস প্রজেক্ট আইডি খুঁজে পেতে পারেন:

d6d0429b700d2b21.png সম্পর্কে

১৬. ফাংশন পরীক্ষাগুলি দেখুন

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

একটি কার্ট তৈরি করুন

ক্লাউড ফাংশনগুলি একটি বিশ্বস্ত সার্ভার পরিবেশে চলে এবং অ্যাডমিন SDK দ্বারা ব্যবহৃত পরিষেবা অ্যাকাউন্ট প্রমাণীকরণ ব্যবহার করতে পারে। প্রথমে, আপনি initializeAdminApp ব্যবহার করে একটি অ্যাপ চালু করবেন initializeApp এর পরিবর্তে। তারপর, আপনি কার্টের জন্য একটি DocumentReference তৈরি করবেন যেখানে আমরা আইটেম যোগ করব এবং কার্টটি চালু করব:

it("should sum the cost of their items", async () => {
    const db = firebase
        .initializeAdminApp({ projectId: REAL_FIREBASE_PROJECT_ID })
        .firestore();

    // Setup: Initialize cart
    const aliceCartRef = db.doc("carts/alice")
    await aliceCartRef.set({ ownerUID: "alice", totalPrice: 0 });

    ...
  });

ফাংশনটি ট্রিগার করুন

তারপর, ফাংশনটি ট্রিগার করার জন্য আমাদের কার্ট ডকুমেন্টের items সাবকালেকশনে ডকুমেন্ট যোগ করুন। ফাংশনে যে যোগটি ঘটে তা পরীক্ষা করে দেখছেন তা নিশ্চিত করতে দুটি আইটেম যোগ করুন।

it("should sum the cost of their items", async () => {
    const db = firebase
        .initializeAdminApp({ projectId: REAL_FIREBASE_PROJECT_ID })
        .firestore();

    // Setup: Initialize cart
    const aliceCartRef = db.doc("carts/alice")
    await aliceCartRef.set({ ownerUID: "alice", totalPrice: 0 });

    //  Trigger calculateCart by adding items to the cart
    const aliceItemsRef = aliceCartRef.collection("items");
    await aliceItemsRef.doc("doc1").set({name: "nectarine", price: 2.99});
    await aliceItemsRef.doc("doc2").set({ name: "grapefruit", price: 6.99 });

    ...
    });
  });

পরীক্ষার প্রত্যাশা সেট করুন

কার্ট ডকুমেন্টে যেকোনো পরিবর্তনের জন্য শ্রোতা নিবন্ধন করতে onSnapshot() ব্যবহার করুন। onSnapshot() একটি ফাংশন প্রদান করে যা আপনি শ্রোতা নিবন্ধন মুক্ত করতে কল করতে পারেন।

এই পরীক্ষার জন্য, দুটি আইটেম যোগ করুন যার একসাথে দাম $9.98। তারপর, পরীক্ষা করুন যে কার্টে প্রত্যাশিত itemCount এবং totalPrice আছে কিনা। যদি তাই হয়, তাহলে ফাংশনটি তার কাজ করেছে।

it("should sum the cost of their items", (done) => {
    const db = firebase
        .initializeAdminApp({ projectId: REAL_FIREBASE_PROJECT_ID })
        .firestore();

    // Setup: Initialize cart
    const aliceCartRef = db.doc("carts/alice")
    aliceCartRef.set({ ownerUID: "alice", totalPrice: 0 });

    //  Trigger calculateCart by adding items to the cart
    const aliceItemsRef = aliceCartRef.collection("items");
    aliceItemsRef.doc("doc1").set({name: "nectarine", price: 2.99});
    aliceItemsRef.doc("doc2").set({ name: "grapefruit", price: 6.99 });
    
    // Listen for every update to the cart. Every time an item is added to
    // the cart's subcollection of items, the function updates `totalPrice`
    // and `itemCount` attributes on the cart.
    // Returns a function that can be called to unsubscribe the listener.
    await new Promise((resolve) => {
      const unsubscribe = aliceCartRef.onSnapshot(snap => {
        // If the function worked, these will be cart's final attributes.
        const expectedCount = 2;
        const expectedTotal = 9.98;
  
        // When the `itemCount`and `totalPrice` match the expectations for the
        // two items added, the promise resolves, and the test passes.
        if (snap.data().itemCount === expectedCount && snap.data().totalPrice == expectedTotal) {
          // Call the function returned by `onSnapshot` to unsubscribe from updates
          unsubscribe();
          resolve();
        };
      });
    });
   });
 });

১৭. পরীক্ষা চালান

আপনার পূর্ববর্তী পরীক্ষাগুলির এমুলেটরগুলি এখনও চালু থাকতে পারে। যদি না থাকে, তাহলে এমুলেটরগুলি শুরু করুন। কমান্ড লাইন থেকে, চালান

$ firebase emulators:start --import=./seed

একটি নতুন টার্মিনাল ট্যাব খুলুন (এমুলেটরগুলি চালু রাখুন) এবং ফাংশন ডিরেক্টরিতে যান। নিরাপত্তা নিয়ম পরীক্ষা থেকে এটি এখনও খোলা থাকতে পারে।

$ cd functions

এখন ইউনিট পরীক্ষা চালান, আপনি মোট ৫টি পরীক্ষা দেখতে পাবেন:

$ npm test

> functions@ test .../emulators-codelab/codelab-initial-state/functions
> mocha

  shopping cart creation
    ✓ can be created by the cart owner (82ms)

  shopping cart reads, updates, and deletes
    ✓ cart can be read by the cart owner (42ms)

  shopping cart items
    ✓ items can be read by the cart owner (40ms)
    ✓ items can be added by the cart owner

  adding an item to the cart recalculates the cart total. 
    1) should sum the cost of their items

  4 passing (2s)
  1 failing

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

১৮. একটি ফাংশন লিখ

এই পরীক্ষাটি ঠিক করার জন্য, আপনাকে functions/index.js এ ফাংশনটি আপডেট করতে হবে। যদিও এই ফাংশনের কিছু অংশ লেখা আছে, এটি সম্পূর্ণ নয়। বর্তমানে ফাংশনটি এইরকম দেখাচ্ছে:

// Recalculates the total cost of a cart; triggered when there's a change
// to any items in a cart.
exports.calculateCart = functions
    .firestore.document("carts/{cartId}/items/{itemId}")
    .onWrite(async (change, context) => {
      console.log(`onWrite: ${change.after.ref.path}`);
      if (!change.after.exists) {
        // Ignore deletes
        return;
      }

      let totalPrice = 125.98;
      let itemCount = 8;
      try {
        
        const cartRef = db.collection("carts").doc(context.params.cartId);

        await cartRef.update({
          totalPrice,
          itemCount
        });
      } catch(err) {
      }
    });

ফাংশনটি সঠিকভাবে কার্ট রেফারেন্স সেট করছে, কিন্তু তারপর totalPrice এবং itemCount এর মান গণনা করার পরিবর্তে, এটি হার্ডকোডেড মানগুলিতে আপডেট করে।

আনুন এবং এর মাধ্যমে পুনরাবৃত্তি করুন

items উপ-সংগ্রহ

একটি নতুন ধ্রুবক, itemsSnap , শুরু করুন, যা items উপ-সংগ্রহ হবে। তারপর, সংগ্রহের সমস্ত নথি পুনরাবৃত্তি করুন।

// Recalculates the total cost of a cart; triggered when there's a change
// to any items in a cart.
exports.calculateCart = functions
    .firestore.document("carts/{cartId}/items/{itemId}")
    .onWrite(async (change, context) => {
      console.log(`onWrite: ${change.after.ref.path}`);
      if (!change.after.exists) {
        // Ignore deletes
        return;
      }


      try {
        let totalPrice = 125.98;
        let itemCount = 8;

        const cartRef = db.collection("carts").doc(context.params.cartId);
        // ADD LINES FROM HERE
        const itemsSnap = await cartRef.collection("items").get();

        itemsSnap.docs.forEach(item => {
          const itemData = item.data();
        })
        // TO HERE
       
        return cartRef.update({
          totalPrice,
          itemCount
        });
      } catch(err) {
      }
    });

মোট মূল্য এবং আইটেমের সংখ্যা গণনা করুন

প্রথমে, totalPrice এবং itemCount এর মান শূন্যে ইনিশিয়ালাইজ করা যাক।

তারপর, আমাদের পুনরাবৃত্তি ব্লকে লজিক যোগ করুন। প্রথমে, আইটেমটির একটি মূল্য আছে কিনা তা পরীক্ষা করুন। যদি আইটেমটির পরিমাণ নির্দিষ্ট না থাকে, তাহলে এটিকে ডিফল্টভাবে 1 এ রাখুন। তারপর, itemCount এর চলমান মোট পরিমাণের সাথে পরিমাণ যোগ করুন। অবশেষে, আইটেমের মূল্যকে পরিমাণ দ্বারা গুণ করে totalPrice এর চলমান মোট পরিমাণের সাথে যোগ করুন:

// Recalculates the total cost of a cart; triggered when there's a change
// to any items in a cart.
exports.calculateCart = functions
    .firestore.document("carts/{cartId}/items/{itemId}")
    .onWrite(async (change, context) => {
      console.log(`onWrite: ${change.after.ref.path}`);
      if (!change.after.exists) {
        // Ignore deletes
        return;
      }

      try {
        // CHANGE THESE LINES
        let totalPrice = 0;
        let itemCount = 0;

        const cartRef = db.collection("carts").doc(context.params.cartId);
        const itemsSnap = await cartRef.collection("items").get();

        itemsSnap.docs.forEach(item => {
          const itemData = item.data();
          // ADD LINES FROM HERE
          if (itemData.price) {
            // If not specified, the quantity is 1
            const quantity = itemData.quantity ? itemData.quantity : 1;
            itemCount += quantity;
            totalPrice += (itemData.price * quantity);
          }
          // TO HERE
        })

        await cartRef.update({
          totalPrice,
          itemCount
        });
      } catch(err) {
      }
    });

সাফল্য এবং ত্রুটির অবস্থা ডিবাগ করতে আপনি লগিং যোগ করতে পারেন:

// Recalculates the total cost of a cart; triggered when there's a change
// to any items in a cart.
exports.calculateCart = functions
    .firestore.document("carts/{cartId}/items/{itemId}")
    .onWrite(async (change, context) => {
      console.log(`onWrite: ${change.after.ref.path}`);
      if (!change.after.exists) {
        // Ignore deletes
        return;
      }

      let totalPrice = 0;
      let itemCount = 0;
      try {
        const cartRef = db.collection("carts").doc(context.params.cartId);
        const itemsSnap = await cartRef.collection("items").get();

        itemsSnap.docs.forEach(item => {
          const itemData = item.data();
          if (itemData.price) {
            // If not specified, the quantity is 1
            const quantity = (itemData.quantity) ? itemData.quantity : 1;
            itemCount += quantity;
            totalPrice += (itemData.price * quantity);
          }
        });

        await cartRef.update({
          totalPrice,
          itemCount
        });

        // OPTIONAL LOGGING HERE
        console.log("Cart total successfully recalculated: ", totalPrice);
      } catch(err) {
        // OPTIONAL LOGGING HERE
        console.warn("update error", err);
      }
    });

১৯. পরীক্ষা পুনরায় চালান

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

$ npm test
> functions@ test .../emulators-codelab/codelab-initial-state/functions
> mocha

  shopping cart creation
    ✓ can be created by the cart owner (306ms)

  shopping cart reads, updates, and deletes
    ✓ cart can be read by the cart owner (59ms)

  shopping cart items
    ✓ items can be read by the cart owner
    ✓ items can be added by the cart owner

  adding an item to the cart recalculates the cart total. 
    ✓ should sum the cost of their items (800ms)


  5 passing (1s)

ভালো করেছো!

২০. স্টোরফ্রন্ট UI ব্যবহার করে এটি ব্যবহার করে দেখুন।

চূড়ান্ত পরীক্ষার জন্য, ওয়েব অ্যাপে ফিরে যান ( http://127.0.0.1:5000/ ) এবং কার্টে একটি আইটেম যোগ করুন।

69ad26cee520bf24.png সম্পর্কে

কার্টটি সঠিক মোট সংখ্যার সাথে আপডেট হয়েছে কিনা তা নিশ্চিত করুন। অসাধারণ!

সংক্ষিপ্তসার

আপনি Firebase এবং Cloud Firestore-এর জন্য Cloud Functions-এর মধ্যে একটি জটিল পরীক্ষার মধ্য দিয়ে গেছেন। পরীক্ষাটি পাস করার জন্য আপনি একটি Cloud Function লিখেছেন। আপনি UI-তে নতুন কার্যকারিতা কাজ করছে কিনা তাও নিশ্চিত করেছেন! আপনি এই সব স্থানীয়ভাবে করেছেন, আপনার নিজস্ব মেশিনে এমুলেটরগুলি চালিয়ে।

আপনি এমন একটি ওয়েব ক্লায়েন্টও তৈরি করেছেন যা স্থানীয় এমুলেটরগুলির বিরুদ্ধে চলছে, ডেটা সুরক্ষিত করার জন্য সুরক্ষা নিয়মগুলি তৈরি করেছে এবং স্থানীয় এমুলেটরগুলি ব্যবহার করে সুরক্ষা নিয়মগুলি পরীক্ষা করেছে।

অনুসরণ