1. আপনি শুরু করার আগে
ক্লাউড ফায়ারস্টোর এবং ক্লাউড ফাংশনগুলির মতো সার্ভারহীন ব্যাকএন্ড সরঞ্জামগুলি ব্যবহার করা খুব সহজ, তবে পরীক্ষা করা কঠিন হতে পারে। ফায়ারবেস স্থানীয় এমুলেটর স্যুট আপনাকে এই পরিষেবাগুলির স্থানীয় সংস্করণগুলি আপনার ডেভেলপমেন্ট মেশিনে চালানোর অনুমতি দেয় যাতে আপনি দ্রুত এবং নিরাপদে আপনার অ্যাপটি বিকাশ করতে পারেন।
পূর্বশর্ত
- একটি সাধারণ সম্পাদক যেমন ভিজ্যুয়াল স্টুডিও কোড, এটম বা সাবলাইম টেক্সট
- Node.js 10.0.0 বা উচ্চতর (Node.js ইনস্টল করতে, nvm ব্যবহার করুন , আপনার সংস্করণ পরীক্ষা করতে,
node --version
চালান) - Java 7 বা উচ্চতর (জাভা ইনস্টল করতে এই নির্দেশাবলী ব্যবহার করুন , আপনার সংস্করণ পরীক্ষা করতে,
java -version
চালান)
আপনি কি করবেন
এই কোডল্যাবে, আপনি একটি সাধারণ অনলাইন শপিং অ্যাপ চালাবেন এবং ডিবাগ করবেন যা একাধিক ফায়ারবেস পরিষেবা দ্বারা চালিত:
- ক্লাউড ফায়ারস্টোর: একটি বিশ্বব্যাপী মাপযোগ্য, সার্ভারহীন, রিয়েল-টাইম ক্ষমতা সহ NoSQL ডাটাবেস।
- ক্লাউড ফাংশন : একটি সার্ভারহীন ব্যাকএন্ড কোড যা ইভেন্ট বা HTTP অনুরোধের প্রতিক্রিয়া হিসাবে চলে।
- ফায়ারবেস প্রমাণীকরণ : একটি পরিচালিত প্রমাণীকরণ পরিষেবা যা অন্যান্য ফায়ারবেস পণ্যগুলির সাথে সংহত করে।
- ফায়ারবেস হোস্টিং : ওয়েব অ্যাপের জন্য দ্রুত এবং নিরাপদ হোস্টিং।
স্থানীয় উন্নয়ন সক্ষম করতে আপনি অ্যাপটিকে এমুলেটর স্যুটের সাথে সংযুক্ত করবেন।
আপনি কীভাবে করবেন তাও শিখবেন:
- কীভাবে আপনার অ্যাপটিকে এমুলেটর স্যুটের সাথে সংযুক্ত করবেন এবং কীভাবে বিভিন্ন এমুলেটর সংযুক্ত হবে।
- ফায়ারবেস সিকিউরিটি রুলস কিভাবে কাজ করে এবং কিভাবে একটি স্থানীয় এমুলেটরের বিরুদ্ধে ফায়ারস্টোর সিকিউরিটি রুলস পরীক্ষা করা যায়।
- কিভাবে একটি ফায়ারবেস ফাংশন লিখতে হয় যা ফায়ারস্টোর ইভেন্ট দ্বারা ট্রিগার হয় এবং কীভাবে এমুলেটর স্যুটের বিপরীতে চলা ইন্টিগ্রেশন পরীক্ষা লিখতে হয়।
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 ../
Firebase CLI পান
এমুলেটর স্যুট হল Firebase CLI (কমান্ড-লাইন ইন্টারফেস) এর অংশ যা আপনার মেশিনে নিম্নলিখিত কমান্ড দিয়ে ইনস্টল করা যেতে পারে:
$ npm install -g firebase-tools
এরপর, নিশ্চিত করুন যে আপনার কাছে CLI এর সর্বশেষ সংস্করণ রয়েছে। এই কোডল্যাবটি 9.0.0 বা উচ্চতর সংস্করণের সাথে কাজ করা উচিত তবে পরবর্তী সংস্করণগুলিতে আরও বাগ সংশোধন অন্তর্ভুক্ত রয়েছে।
$ firebase --version 9.6.0
আপনার Firebase প্রকল্পের সাথে সংযোগ করুন
আপনার যদি Firebase প্রকল্প না থাকে, Firebase কনসোলে , একটি নতুন Firebase প্রকল্প তৈরি করুন৷ আপনি যে প্রজেক্ট আইডিটি বেছে নিয়েছেন তার একটি নোট করুন, আপনার এটি পরে প্রয়োজন হবে।
এখন আমাদের এই কোডটি আপনার Firebase প্রকল্পের সাথে সংযুক্ত করতে হবে। প্রথমে Firebase CLI-তে লগ ইন করতে নিম্নলিখিত কমান্ডটি চালান:
$ firebase login
পরবর্তীতে একটি প্রকল্প উপনাম তৈরি করতে নিম্নলিখিত কমান্ডটি চালান। আপনার Firebase প্রকল্পের ID দিয়ে $YOUR_PROJECT_ID
প্রতিস্থাপন করুন।
$ firebase use $YOUR_PROJECT_ID
এখন আপনি অ্যাপটি চালানোর জন্য প্রস্তুত!
3. এমুলেটর চালান
এই বিভাগে, আপনি স্থানীয়ভাবে অ্যাপটি চালাবেন। এর মানে হল এমুলেটর স্যুট বুট করার সময়।
এমুলেটরগুলি শুরু করুন
কোডল্যাব সোর্স ডিরেক্টরির ভিতর থেকে, এমুলেটরগুলি শুরু করতে নিম্নলিখিত কমান্ডটি চালান:
$ 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.
একবার আপনি সমস্ত এমুলেটর স্টার্ট মেসেজ দেখতে পেলে, অ্যাপটি ব্যবহারের জন্য প্রস্তুত।
এমুলেটরগুলির সাথে ওয়েব অ্যাপটিকে সংযুক্ত করুন৷
লগের টেবিলের উপর ভিত্তি করে আমরা দেখতে পাচ্ছি যে ক্লাউড ফায়ারস্টোর এমুলেটর পোর্ট 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 উদাহরণগুলিতে অ্যাক্সেস করে:
public/js/homepage.js
const auth = firebaseApp.auth();
const db = firebaseApp.firestore();
আসুন স্থানীয় এমুলেটরগুলির দিকে নির্দেশ করতে db
এবং auth
অবজেক্টগুলি আপডেট করি:
public/js/homepage.js
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);
}
এখন যখন অ্যাপটি আপনার স্থানীয় মেশিনে চলছে (হোস্টিং এমুলেটর দ্বারা পরিবেশিত) ফায়ারস্টোর ক্লায়েন্ট একটি প্রোডাকশন ডাটাবেসের পরিবর্তে স্থানীয় এমুলেটরের দিকে নির্দেশ করে।
EmulatorUI খুলুন
আপনার ওয়েব ব্রাউজারে, http://127.0.0.1:4000/ এ নেভিগেট করুন। আপনার এমুলেটর স্যুট UI দেখতে হবে।
Firestore এমুলেটরের জন্য UI দেখতে ক্লিক করুন। --import
পতাকার সাথে আমদানি করা ডেটার কারণে items
সংগ্রহে ইতিমধ্যেই ডেটা রয়েছে৷
4. অ্যাপটি চালান
অ্যাপটি খুলুন
আপনার ওয়েব ব্রাউজারে, http://127.0.0.1:5000- এ নেভিগেট করুন এবং আপনার মেশিনে স্থানীয়ভাবে ফায়ার স্টোর চলছে দেখতে পাবেন!
অ্যাপটি ব্যবহার করুন
হোমপেজে একটি আইটেম বাছাই করুন এবং কার্টে যোগ করুন ক্লিক করুন। দুর্ভাগ্যবশত, আপনি নিম্নলিখিত ত্রুটির মধ্যে চালাবেন:
এর যে বাগ ঠিক করা যাক! যেহেতু সবকিছু এমুলেটরগুলিতে চলছে, আমরা পরীক্ষা করতে পারি এবং বাস্তব ডেটাকে প্রভাবিত করার বিষয়ে চিন্তা করতে পারি না।
5. অ্যাপটি ডিবাগ করুন
বাগ খুঁজুন
ঠিক আছে ক্রোম ডেভেলপার কনসোলে দেখা যাক। কনসোলে ত্রুটি দেখতে Control+Shift+J
(Windows, Linux, Chrome OS) বা Command+Option+J
(Mac) টিপুন:
addToCart
পদ্ধতিতে কিছু ত্রুটি ছিল বলে মনে হচ্ছে, আসুন এটি একবার দেখে নেওয়া যাক। যেখানে আমরা সেই পদ্ধতিতে uid
নামক কিছু অ্যাক্সেস করার চেষ্টা করি এবং কেন এটি null
হবে? এই মুহূর্তে পদ্ধতিটি public/js/homepage.js
এ এইরকম দেখাচ্ছে:
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);
}
আহা! আমরা অ্যাপে সাইন ইন করিনি। ফায়ারবেস প্রমাণীকরণ ডক্স অনুসারে, যখন আমরা সাইন ইন করি না, তখন auth.currentUser
null
। এর জন্য একটি চেক যোগ করা যাক:
public/js/homepage.js
addToCart(id, itemData) {
// ADD THESE LINES
if (this.auth.currentUser === null) {
this.showError("You must be signed in!");
return;
}
// ...
}
অ্যাপটি পরীক্ষা করুন
এখন, পৃষ্ঠাটি রিফ্রেশ করুন এবং তারপরে কার্টে যোগ করুন ক্লিক করুন। আপনি এই সময় একটি সুন্দর ত্রুটি পেতে হবে:
কিন্তু আপনি যদি উপরের টুলবারে সাইন ইন ক্লিক করেন এবং তারপর আবার Add to Cart-এ ক্লিক করেন, আপনি দেখতে পাবেন যে কার্ট আপডেট হয়েছে।
যাইহোক, সংখ্যাগুলি মোটেও সঠিক বলে মনে হচ্ছে না:
চিন্তা করবেন না, আমরা শীঘ্রই সেই বাগটি ঠিক করব৷ প্রথমে, আপনি আপনার কার্টে একটি আইটেম যোগ করার সময় আসলে কী ঘটেছিল তার গভীরে ডুব দিন।
6. স্থানীয় ফাংশন ট্রিগার
কার্টে যোগ করুন ক্লিক করলে একাধিক এমুলেটর জড়িত ইভেন্টের একটি শৃঙ্খল শুরু হয়। Firebase CLI লগগুলিতে, আপনি আপনার কার্টে একটি আইটেম যুক্ত করার পরে নিম্নলিখিত বার্তাগুলির মতো কিছু দেখতে পাবেন:
i functions: Beginning execution of "calculateCart" i functions: Finished "calculateCart" in ~1s
সেই লগগুলি এবং আপনি যে UI আপডেট দেখেছেন তা তৈরি করতে চারটি মূল ঘটনা ঘটেছে:
1) Firestore লিখুন - ক্লায়েন্ট
Firestore সংগ্রহে একটি নতুন নথি যোগ করা হয়েছে /carts/{cartId}/items/{itemId}/
। আপনি public/js/homepage.js
ভিতরে addToCart
ফাংশনে এই কোডটি দেখতে পারেন:
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);
}
2) ক্লাউড ফাংশন ট্রিগার
ক্লাউড ফাংশন calculateCart
onWrite
ট্রিগার ব্যবহার করে কার্ট আইটেমগুলির সাথে ঘটতে পারে এমন কোনও লেখার ইভেন্ট (তৈরি, আপডেট বা মুছে ফেলা) শোনে, যা আপনি functions/index.js
এ দেখতে পারেন :
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) {
}
}
);
3) Firestore লিখুন - অ্যাডমিন
calculateCart
ফাংশন কার্টের সমস্ত আইটেম পড়ে এবং মোট পরিমাণ এবং মূল্য যোগ করে, তারপর এটি "কার্ট" নথিটিকে নতুন মোটের সাথে আপডেট করে (উপরে cartRef.update(...)
দেখুন)।
4) ফায়ারস্টোর রিড - ক্লায়েন্ট
ওয়েব ফ্রন্টএন্ড কার্টে পরিবর্তন সম্পর্কে আপডেট পেতে সাবস্ক্রাইব করা হয়েছে। ক্লাউড ফাংশন নতুন মোট লেখার পরে এবং UI আপডেট করার পরে এটি একটি রিয়েল-টাইম আপডেট পায়, যেমন আপনি public/js/homepage.js
এ দেখতে পারেন :
public/js/homepage.js
this.cartUnsub = cartRef.onSnapshot(cart => {
// The cart document was changed, update the UI
// ...
});
রিক্যাপ
চমৎকার কাজ! আপনি এইমাত্র একটি সম্পূর্ণ স্থানীয় অ্যাপ সেট আপ করেছেন যা সম্পূর্ণ স্থানীয় পরীক্ষার জন্য তিনটি ভিন্ন ফায়ারবেস এমুলেটর ব্যবহার করে।
কিন্তু অপেক্ষা করুন, আরো আছে! পরবর্তী বিভাগে আপনি শিখবেন:
- ফায়ারবেস এমুলেটর ব্যবহার করে এমন ইউনিট পরীক্ষা কীভাবে লিখবেন।
- আপনার নিরাপত্তা নিয়ম ডিবাগ করতে ফায়ারবেস এমুলেটরগুলি কীভাবে ব্যবহার করবেন।
7. আপনার অ্যাপের জন্য উপযোগী নিরাপত্তা নিয়ম তৈরি করুন
আমাদের ওয়েব অ্যাপ ডেটা পড়ে এবং লেখে কিন্তু এখনও পর্যন্ত আমরা নিরাপত্তা নিয়ে মোটেও চিন্তিত নই। ক্লাউড ফায়ারস্টোর "নিরাপত্তা বিধি" নামে একটি সিস্টেম ব্যবহার করে ঘোষণা করে যে কার ডেটা পড়তে এবং লেখার অ্যাক্সেস রয়েছে৷ এমুলেটর স্যুট এই নিয়মগুলির প্রোটোটাইপ করার একটি দুর্দান্ত উপায়।
এডিটরে, 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;
}
}
}
8. এমুলেটর এবং পরীক্ষা চালান
এমুলেটরগুলি শুরু করুন
কমান্ড লাইনে, নিশ্চিত করুন যে আপনি 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
এই মুহূর্তে আমাদের চারটি ব্যর্থতা রয়েছে। আপনি নিয়ম ফাইল তৈরি করার সাথে সাথে, আপনি আরও পরীক্ষা পাস দেখে অগ্রগতি পরিমাপ করতে পারেন।
9. নিরাপদ কার্ট অ্যাক্সেস
প্রথম দুটি ব্যর্থতা হল "শপিং কার্ট" পরীক্ষা যা পরীক্ষা করে:
- ব্যবহারকারীরা শুধুমাত্র তাদের নিজস্ব কার্ট তৈরি এবং আপডেট করতে পারেন
- ব্যবহারকারীরা শুধুমাত্র তাদের নিজস্ব কার্ট পড়তে পারেন
functions/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}
এর মধ্যে বিবৃতি আপডেট করুন :
firestore.rules
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 প্রমাণীকরণ ব্যবহার করে, তাহলে
request.auth
অবজেক্ট সেই ব্যবহারকারীর বর্ণনা করে যে অনুরোধটি করছে।
10. টেস্ট কার্ট অ্যাক্সেস
যখনই firestore.rules
সংরক্ষণ করা হয় তখন এমুলেটর স্যুট স্বয়ংক্রিয়ভাবে নিয়ম আপডেট করে। আপনি নিশ্চিত করতে পারেন যে এমুলেটরটি নিয়মগুলি আপডেট করেছে বার্তাটির জন্য এমুলেটর চালানো ট্যাবটি দেখে Rules updated
:
পরীক্ষাগুলি পুনরায় চালান এবং পরীক্ষা করুন যে প্রথম দুটি পরীক্ষা এখন পাস করেছে:
$ 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
ভাল করেছ! আপনি এখন শপিং কার্টে অ্যাক্সেস সুরক্ষিত করেছেন। আসুন পরবর্তী ব্যর্থ পরীক্ষায় এগিয়ে যাই।
11. UI-তে "Add to Cart" ফ্লো পরীক্ষা করুন৷
এই মুহুর্তে, যদিও কার্টের মালিকরা তাদের কার্টে পড়ে এবং লিখতে পারে, তারা তাদের কার্টে পৃথক আইটেম পড়তে বা লিখতে পারে না। কারণ যখন মালিকদের কার্ট নথিতে অ্যাক্সেস থাকে, তাদের কার্টের আইটেম সাবকলেকশনে অ্যাক্সেস থাকে না।
এটি ব্যবহারকারীদের জন্য একটি ভাঙা অবস্থা।
ওয়েব UI-তে ফিরে যান, যা http://127.0.0.1:5000,
এবং আপনার কার্টে কিছু যোগ করার চেষ্টা করুন। আপনি একটি Permission Denied
ত্রুটি পাবেন, ডিবাগ কনসোল থেকে দৃশ্যমান, কারণ আমরা এখনও items
সাবকলেকশনে তৈরি নথিগুলিতে ব্যবহারকারীদের অ্যাক্সেস মঞ্জুর করিনি৷
12. কার্ট আইটেম অ্যাক্সেসের অনুমতি দিন
এই দুটি পরীক্ষা নিশ্চিত করে যে ব্যবহারকারীরা শুধুমাত্র তাদের নিজস্ব কার্ট থেকে আইটেম যোগ করতে বা পড়তে পারেন:
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
}));
});
সুতরাং আমরা একটি নিয়ম লিখতে পারি যা অ্যাক্সেসের অনুমতি দেয় যদি বর্তমান ব্যবহারকারীর কার্ট নথিতে মালিকের ইউআইডির মতো একই ইউআইডি থাকে। যেহেতু create, update, delete
জন্য বিভিন্ন নিয়ম নির্দিষ্ট করার প্রয়োজন নেই, আপনি একটি write
নিয়ম ব্যবহার করতে পারেন, যা ডেটা পরিবর্তন করে এমন সমস্ত অনুরোধের ক্ষেত্রে প্রযোজ্য।
আইটেম সাবকলেকশনে নথিগুলির জন্য নিয়ম আপডেট করুন। শর্তসাপেক্ষে get
হল Firestore থেকে একটি মান পড়া—এই ক্ষেত্রে, কার্ট নথিতে 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;
}
// ...
}
}
13. টেস্ট কার্ট আইটেম অ্যাক্সেস
এখন আমরা পরীক্ষা পুনরায় চালাতে পারি। আউটপুটের শীর্ষে স্ক্রোল করুন এবং আরও পরীক্ষা পাস হয়েছে কিনা তা পরীক্ষা করুন:
$ 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
চমৎকার! এখন আমাদের সব পরীক্ষা পাস। আমাদের একটি মুলতুবি পরীক্ষা আছে, কিন্তু আমরা কয়েক ধাপে সেটিতে পৌঁছাতে পারব।
14. আবার "কার্টে যোগ করুন" প্রবাহ পরীক্ষা করুন
ওয়েব ফ্রন্ট এন্ডে ফিরে যান ( http://127.0.0.1:5000 ) এবং কার্টে একটি আইটেম যোগ করুন। আমাদের পরীক্ষা এবং নিয়মগুলি ক্লায়েন্টের প্রয়োজনীয় কার্যকারিতার সাথে মেলে তা নিশ্চিত করার জন্য এটি একটি গুরুত্বপূর্ণ পদক্ষেপ। (মনে রাখবেন যে শেষবার আমরা চেষ্টা করে দেখেছিলাম UI ব্যবহারকারীরা তাদের কার্টে আইটেম যোগ করতে পারেনি!)
firestore.rules
সংরক্ষিত হলে ক্লায়েন্ট স্বয়ংক্রিয়ভাবে নিয়মগুলি পুনরায় লোড করে। তাই, কার্টে কিছু যোগ করার চেষ্টা করুন।
রিক্যাপ
চমৎকার কাজ! আপনি এইমাত্র আপনার অ্যাপের নিরাপত্তা উন্নত করেছেন, এটিকে উৎপাদনের জন্য প্রস্তুত করার জন্য একটি অপরিহার্য পদক্ষেপ! যদি এটি একটি প্রোডাকশন অ্যাপ হয়, তাহলে আমরা আমাদের ক্রমাগত ইন্টিগ্রেশন পাইপলাইনে এই পরীক্ষাগুলি যোগ করতে পারতাম। এটি আমাদের সামনে এগিয়ে যাওয়ার আস্থা দেবে যে আমাদের শপিং কার্টের ডেটাতে এই অ্যাক্সেস নিয়ন্ত্রণ থাকবে, এমনকি অন্যরা নিয়মগুলি পরিবর্তন করলেও।
কিন্তু অপেক্ষা করুন, আরো আছে!
আপনি চালিয়ে গেলে আপনি শিখবেন:
- ফায়ারস্টোর ইভেন্ট দ্বারা ট্রিগার করা একটি ফাংশন কীভাবে লিখবেন
- একাধিক এমুলেটর জুড়ে কাজ করে এমন পরীক্ষাগুলি কীভাবে তৈরি করবেন
15. ক্লাউড ফাংশন পরীক্ষা সেট আপ করুন৷
এখন পর্যন্ত আমরা আমাদের ওয়েব অ্যাপের ফ্রন্টএন্ড এবং ফায়ারস্টোর নিরাপত্তা বিধিতে ফোকাস করেছি। কিন্তু এই অ্যাপটি ব্যবহারকারীর কার্ট আপ টু ডেট রাখতে ক্লাউড ফাংশন ব্যবহার করে, তাই আমরা সেই কোডটিও পরীক্ষা করতে চাই।
এমুলেটর স্যুট ক্লাউড ফাংশনগুলি পরীক্ষা করা খুব সহজ করে তোলে, এমনকি ফাংশনগুলি যা ক্লাউড ফায়ারস্টোর এবং অন্যান্য পরিষেবাগুলি ব্যবহার করে৷
এডিটরে, 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
ভেরিয়েবলটি খুঁজুন এবং এটিকে আপনার আসল ফায়ারবেস প্রকল্প আইডিতে পরিবর্তন করুন৷:
// CHANGE THIS LINE
const REAL_FIREBASE_PROJECT_ID = "changeme";
আপনি যদি আপনার প্রোজেক্ট আইডি ভুলে যান তাহলে আপনি Firebase কনসোলের প্রোজেক্ট সেটিংসে আপনার Firebase প্রোজেক্ট আইডি খুঁজে পেতে পারেন:
16. ফাংশন পরীক্ষার মাধ্যমে হাঁটুন
যেহেতু এই পরীক্ষাটি ক্লাউড ফায়ারস্টোর এবং ক্লাউড ফাংশনগুলির মধ্যে মিথস্ক্রিয়াকে বৈধ করে, এটি পূর্ববর্তী কোডল্যাবগুলির পরীক্ষার তুলনায় আরও বেশি সেটআপ জড়িত৷ আসুন এই পরীক্ষার মধ্য দিয়ে হেঁটে যাই এবং এটি কী প্রত্যাশা করে তার একটি ধারণা পান।
একটি কার্ট তৈরি করুন
ক্লাউড ফাংশনগুলি একটি বিশ্বস্ত সার্ভার পরিবেশে চলে এবং অ্যাডমিন SDK দ্বারা ব্যবহৃত পরিষেবা অ্যাকাউন্ট প্রমাণীকরণ ব্যবহার করতে পারে৷ প্রথমে, আপনি initializeApp
পরিবর্তে initializeAdminApp
ব্যবহার করে একটি অ্যাপ আরম্ভ করুন। তারপর, আপনি কার্টের জন্য একটি ডকুমেন্ট রেফারেন্স তৈরি করুন আমরা আইটেম যোগ করব এবং কার্টটি শুরু করব:
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();
};
});
});
});
});
17. পরীক্ষা চালান
আপনি এখনও পূর্ববর্তী পরীক্ষা থেকে এমুলেটর চলমান থাকতে পারে. যদি না হয়, এমুলেটরগুলি শুরু করুন। কমান্ড লাইন থেকে, চালান
$ firebase emulators:start --import=./seed
একটি নতুন টার্মিনাল ট্যাব খুলুন (এমুলেটরগুলি চলমান রেখে দিন) এবং ফাংশন ডিরেক্টরিতে যান। আপনি এখনও নিরাপত্তা নিয়ম পরীক্ষা থেকে এটি খোলা থাকতে পারে.
$ cd functions
এখন ইউনিট পরীক্ষা চালান, আপনার মোট 5 টি পরীক্ষা দেখতে হবে:
$ 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
আপনি যদি নির্দিষ্ট ব্যর্থতার দিকে তাকান তবে এটি একটি টাইমআউট ত্রুটি বলে মনে হচ্ছে। এটি কারণ পরীক্ষাটি সঠিকভাবে আপডেট করার জন্য ফাংশনটির জন্য অপেক্ষা করছে, তবে এটি কখনই হয় না। এখন, আমরা পরীক্ষাটি সন্তুষ্ট করার জন্য ফাংশনটি লিখতে প্রস্তুত।
18. একটি ফাংশন লিখুন
এই পরীক্ষাটি ঠিক করতে, আপনাকে 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
উপসংগ্রহ
items
উপ-সংগ্রহ হতে একটি নতুন ধ্রুবক, itemsSnap
শুরু করুন। তারপর, সংগ্রহের সমস্ত নথির মাধ্যমে পুনরাবৃত্তি করুন।
// 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);
}
});
19. পরীক্ষা পুনরায় চালান
কমান্ড লাইনে, নিশ্চিত করুন যে এমুলেটরগুলি এখনও চলছে এবং পরীক্ষাগুলি পুনরায় চালান। আপনাকে এমুলেটরগুলি পুনরায় চালু করার দরকার নেই কারণ তারা স্বয়ংক্রিয়ভাবে ফাংশনে পরিবর্তনগুলি গ্রহণ করে। আপনি সব পরীক্ষা পাস দেখতে হবে:
$ 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)
ভাল করেছ!
20. স্টোরফ্রন্ট UI ব্যবহার করে এটি ব্যবহার করে দেখুন
চূড়ান্ত পরীক্ষার জন্য, ওয়েব অ্যাপে ফিরে যান ( http://127.0.0.1:5000/ ) এবং কার্টে একটি আইটেম যোগ করুন।
নিশ্চিত করুন যে কার্টটি সঠিক মোটের সাথে আপডেট হয়েছে। চমত্কার!
রিক্যাপ
আপনি ফায়ারবেস এবং ক্লাউড ফায়ারস্টোরের জন্য ক্লাউড ফাংশনগুলির মধ্যে একটি জটিল পরীক্ষার মধ্যে দিয়ে গেছেন৷ পরীক্ষা পাস করার জন্য আপনি একটি ক্লাউড ফাংশন লিখেছেন। আপনি নিশ্চিত করেছেন যে নতুন কার্যকারিতা UI এ কাজ করছে! আপনি স্থানীয়ভাবে এই সব করেছেন, আপনার নিজের মেশিনে এমুলেটর চালাচ্ছেন।
আপনি একটি ওয়েব ক্লায়েন্টও তৈরি করেছেন যা স্থানীয় এমুলেটরগুলির বিরুদ্ধে চলছে, ডেটা সুরক্ষিত করার জন্য তৈরি করা সুরক্ষা নিয়মগুলি এবং স্থানীয় এমুলেটরগুলি ব্যবহার করে সুরক্ষা নিয়মগুলি পরীক্ষা করেছে৷