কীভাবে একটি মেনু ফিড তৈরি করবেন

এই টিউটোরিয়ালে আমরা গুগল প্রোটোকল বাফারস (প্রোটোবাফ) ডেফিনিশন ব্যবহার করে JSON-এ একটি ফিড তৈরি করার পদ্ধতি দেখাবো। আমরা প্রোটোবাফ স্কিমার উপর ভিত্তি করে সোর্স কোড তৈরি করতে প্রোটোবাফ কম্পাইলার ব্যবহার করব। তৈরি করা সোর্স কোড ব্যবহার করে ফিড তৈরি করলে একটি সহজ ইন্টারফেস পাওয়া যায় এবং ভুল ফিল্ডের নাম বা ফিল্ড টাইপ দিয়ে ফিড এনটিটি তৈরি হওয়া প্রতিরোধ করা যায়।

প্রজেক্ট সেটআপ

  • একটি নতুন প্রজেক্ট ডিরেক্টরি তৈরি করুন।
  • প্রোটো ডেফিনিশনগুলো থেকে food_menu.proto, money.proto এবং localized_text.proto ফাইলগুলোর বিষয়বস্তু আপনার প্রজেক্ট ডিরেক্টরি রুটে নতুন ফাইল হিসেবে কপি করুন।
  • প্রোটোক কম্পাইলার ইনস্টল করুন

    .proto ফাইলগুলো থেকে সোর্স কোড তৈরি করার জন্য আপনার protoc কম্পাইলারের প্রয়োজন হবে। Protocol Buffers-এর GitHub রিলিজ পেজ থেকে সর্বশেষ প্রি-বিল্ট বাইনারিটি ডাউনলোড করুন।

    zip ফাইলটি এক্সট্র্যাক্ট করুন এবং bin পাথটি PATH এনভায়রনমেন্ট ভেরিয়েবলে যোগ করুন, উদাহরণস্বরূপ:

    unzip protoc-22.0-linux-x86_64.zip -d /usr/local/protoc
    export PATH="$PATH:/usr/local/protoc/bin"
      

সোর্স কোড তৈরি করুন

পাইথন

  1. পাইথন প্রোটোবাফ লাইব্রেরি ইনস্টল করুন
    pip install protobuf
        
  2. ক্লায়েন্ট সোর্স কোড তৈরি করুন।

    প্রোটো আউটপুট ডিরেক্টরি তৈরি করুন: generated

    protoc --python_out=./generated food_menu.proto money.proto localized_text.proto
        

    তৈরি হওয়া পাথটি অন্তর্ভুক্ত করার জন্য PYTHONPATH এনভায়রনমেন্ট ভেরিয়েবলটি আপডেট করুন, উদাহরণস্বরূপ:

    export PYTHONPATH="$PYTHONPATH:./generated"
        

    আপনি যদি Bazel ব্যবহার করেন, তাহলে protoc চালানোর বিকল্প হিসেবে py_proto_library রুলটি ব্যবহার করে দেখুন।

ব্যবহারের উদাহরণ

সম্পূর্ণ নমুনা প্রকল্পটি এখানে পাওয়া যাবে।

"""Menu feed example used in

https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#python.
"""
import json
from generated import food_menu_pb2
from google.protobuf.json_format import MessageToDict

# create feed
feed = food_menu_pb2.FoodMenuFeed()

# add a menu component to feed data
menuComponent = feed.data.add()
menuComponent.menu.menu_id = 'menu1'
menuComponent.menu.merchant_ids.append('dining-1')
menuDisplayName = menuComponent.menu.display_name.text.add()
menuDisplayName.text = 'Menu'
menuDisplayName.language_code = 'en-us'
menuComponent.menu.language = 'en-us'
menuComponent.menu.last_merchant_update_time.seconds = 1633621547

for i in ['appetizers', 'dinner']:
  menuComponent.menu.menu_section_ids.append(i)

# add a menu section component to feed data
sectionComponent = feed.data.add()
sectionComponent.section.menu_section_id = 'appetizers'
sectionDisplayName = sectionComponent.section.display_name.text.add()
sectionDisplayName.text = 'Lunch Appetizers'
sectionDisplayName.language_code = 'en-us'
sectionComponent.section.menu_item_ids.append('breadsticks-sauce')

# add a menu item component to feed data
itemComponent = feed.data.add()
itemComponent.item.menu_item_id = 'breadsticks-sauce'
itemDisplayName = itemComponent.item.display_name.text.add()
itemDisplayName.text = 'Breadsticks & Sauce'
itemDisplayName.language_code = 'en-us'
itemDescription = itemComponent.item.description.text.add()
itemDescription.text = 'Breakfast basket w/ side of tomato sauce (size 6 or 12)'
itemDescription.language_code = 'en-us'

for i in ['breadstick-sm', 'breadstick-lg']:
  itemComponent.item.menu_item_option_set.menu_item_option_ids.append(i)

for i in [
    'http://www.example.com/photos/breadsticks.jpg',
    'http://www.example.com/photos/sauce.jpg',
]:
  itemImage = itemComponent.item.images.add()
  itemImage.uri = i

# add a menu item option component to feed data
optionComponent = feed.data.add()
optionComponent.option.menu_item_option_id: 'breadstick-sm'
optionComponent.option.value.property_type = (
    food_menu_pb2.MenuItemOptionProperty.PropertyType.SIZE
)
optionTextValue = optionComponent.option.value.text_val.text.add()
optionTextValue.text = 'Small'
optionTextValue.language_code = 'en-us'
optionOffer = optionComponent.option.offer_set.offers.add()
optionOffer.price.currency_code = 'USD'
optionOffer.price.units = 8
optionOffer.price.nanos = 0

# Example testing for menu feed size
# Protocol buffer message must be less than 2 GiB
# https://protobuf.dev/programming-guides/proto-limits/
# It is recommended to not exceed 200 MB, as there is an Actions
# Center limit of 200 MB per file after compression.
if feed.ByteSize() > 200 * 1024 * 1024:
		# start new file
    print('starting new file...')

feedJSON = json.dumps(MessageToDict(feed, preserving_proto_field_name=True))
print(feedJSON)

    

এই কোড উদাহরণটিতে দেখানো হয়েছে কীভাবে নিম্নলিখিত প্রতিটি এনটিটির একটি করে নিয়ে একটি ফিড অবজেক্ট তৈরি করতে হয়:

  • মেনু
  • মেনু বিভাগ
  • মেনু আইটেম
  • মেনু আইটেম বিকল্প

এরপর উদাহরণটিতে দেখানো হয়েছে কীভাবে ফিডটিকে JSON-এ সিরিয়ালাইজ করতে হয়।

পাইথন এপিআই প্রোপার্টি সেট করার মাধ্যমে নেস্টেড অবজেক্টের লেজি ইনিশিয়ালাইজেশনের সুযোগ দেয়।

টাইপস্ক্রিপ্ট

  1. TypeScript protoc প্লাগইনটি ইনস্টল করুন।
    npm init
    npm i -D typescript
    npm i ts-proto
          
    উল্লেখ্য যে, ts-proto গুগলের আনুষ্ঠানিকভাবে সমর্থিত কোনো প্রকল্প নয়।
  2. আউটপুট ডিরেক্টরি তৈরি করুন এবং ক্লায়েন্ট সোর্স কোড জেনারেট করুন।

    প্রোটো আউটপুট ডিরেক্টরি তৈরি করুন: src/generated

    protoc --plugin="./node_modules/.bin/protoc-gen-ts_proto" --ts_proto_opt=useOptionals=all --ts_proto_opt=snakeToCamel=false --ts_proto_opt=onlyTypes=true --ts_proto_out="./src/generated" food_menu.proto money.proto localized_text.proto
          

    আপনি যদি Bazel ব্যবহার করেন, তাহলে protoc চালানোর বিকল্প হিসেবে js_proto_library রুলটি ব্যবহার করে দেখুন।

ব্যবহারের উদাহরণ

সম্পূর্ণ নমুনা প্রকল্পটি এখানে পাওয়া যাবে।

import {FoodMenuFeed, Menu, MenuItem, MenuSection, MenuItemOption, MenuItemOptionProperty_PropertyType} from './generated/food_menu';

const menu: Menu = {
  menu_id: 'menu1',
  merchant_ids: ['dining-1'],
  display_name: {text: [{text: 'Menu', language_code: 'en-us'}]},
  language: 'en-us',
  menu_section_ids: ['appetizers', 'dinner'],
  last_merchant_update_time: new Date()
};

const section: MenuSection = {
  menu_section_id: 'appetizers',
  display_name: {text: [{text: 'Lunch Appetizers', language_code: 'en-us'}]},
  menu_section_ids: ['breadsticks-sauce']
};

const item: MenuItem = {
  menu_item_id: 'breadsticks-sauce',
  display_name: {text: [{text: 'Breadsticks & Sauce', language_code: 'en-us'}]},
  description: {
    text: [{
      text: 'Breakfast basket w/ side of tomato sauce (size 6 or 12)',
      language_code: 'en-us'
    }]
  },
  menu_item_option_set:
      {menu_item_option_ids: ['breadstick-sm', 'breadstick-lg']},
  images: [
    {uri: 'http://www.example.com/photos/breadsticks.jpg'},
    {uri: 'http://www.example.com/photos/sauce.jpg'}
  ]
};

const option: MenuItemOption = {
  menu_item_option_id: 'breadstick-sm',
  value: {
    property_type: MenuItemOptionProperty_PropertyType.SIZE,
    text_val: {
      text: [
        {text: "Small", language_code: "en-us"}
      ]
     }
  },
  offer_set: {
    offers: [{
      price: {
        currency_code: "USD",
        units: 8,
        nanos: 0
      }
    }]
  }
};

const feed: FoodMenuFeed = {
  data: [{menu}, {section}, {item}, {option}]
};

// Example testing for menu feed size
// Protocol buffer message must be less than 2 GiB
// https://protobuf.dev/programming-guides/proto-limits/
// It is recommended to not exceed 200 MB, as there is an Actions
// Center limit of 200 MB per file after compression.
if (new Blob([JSON.stringify(feed)]).size > 200 * 1024 * 1024) {
  // create a new file
}

console.log(JSON.stringify(feed));

  

এই কোড উদাহরণটিতে দেখানো হয়েছে কীভাবে নিম্নলিখিত প্রতিটি এনটিটির একটি করে নিয়ে একটি ফিড অবজেক্ট তৈরি করতে হয়:

  • মেনু
  • মেনু বিভাগ
  • মেনু আইটেম
  • মেনু আইটেম বিকল্প

এরপর উদাহরণটিতে দেখানো হয়েছে কীভাবে ফিডটিকে JSON-এ সিরিয়ালাইজ করতে হয়।

জাভা

  1. এখানে বর্ণিত পদ্ধতি অনুযায়ী Maven অথবা gradle ব্যবহার করে আপনার প্রজেক্টে protobuf-java এবং protobuf-java-util ডিপেন্ডেন্সিগুলো যোগ করুন।
  2. ক্লায়েন্ট সোর্স কোড তৈরি করুন।
    protoc --java_out=src/main/java food_menu.proto money.proto localized_text.proto
        

    Maven ব্যবহার করার সময় কম্পাইল টাইমে সোর্স কোড তৈরি করতে আপনি protobuf-maven-plugin ব্যবহার করতে পারেন।

    আপনি যদি Bazel ব্যবহার করেন, তাহলে protoc চালানোর বিকল্প হিসেবে java_proto_library রুলটি ব্যবহার করে দেখুন।

ব্যবহারের উদাহরণ

সম্পূর্ণ নমুনা প্রকল্পটি এখানে পাওয়া যাবে।

package com.example;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Timestamp;
import com.google.type.Money;
import com.google.protobuf.util.JsonFormat;
import com.google.type.LocalizedText;
import food.menu.v1.FoodMenu.FoodMenuFeed;
import food.menu.v1.FoodMenu.Menu;
import food.menu.v1.FoodMenu.MenuComponent;
import food.menu.v1.FoodMenu.MenuItem;
import food.menu.v1.FoodMenu.MenuSection;
import food.menu.v1.FoodMenu.TextField;
import food.menu.v1.FoodMenu.MenuItemOption;
import food.menu.v1.FoodMenu.MenuItemOptionProperty;
import food.menu.v1.FoodMenu.OfferSet;
import food.menu.v1.FoodMenu.Offer;

/**
 * Menu feed example used in
 *
 * <p>https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#java.
 */
public class Feed {

  // 200 MB
  public static final int MAX_BYTES_DATA_FILE = 200 * 1024 * 1024;

  public static void main(String[] args) throws InvalidProtocolBufferException {
    Feed feed = new Feed();
    feed.createMenuFeed();
  }

  public void createMenuFeed() throws InvalidProtocolBufferException {

    Menu.Builder menu =
        Menu.newBuilder()
            .setMenuId("menu1")
            .addMerchantIds("dining-1")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(LocalizedText.newBuilder().setText("Menu").setLanguageCode("en-us")))
            .setLanguage("en-us")
            .setLastMerchantUpdateTime(Timestamp.newBuilder().setSeconds(1633621547));

    MenuSection.Builder section =
        MenuSection.newBuilder()
            .setMenuSectionId("appetizers")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Lunch Appetizers")
                            .setLanguageCode("en-us")))
            .addMenuItemIds("breadsticks-sauce");

    MenuItem.Builder item =
        MenuItem.newBuilder()
            .setMenuItemId("breadsticks-sauce")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Breadsticks & Sauce")
                            .setLanguageCode("en-us")))
            .setDescription(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Breadsticks & Sauce")
                            .setLanguageCode("en-us")));

    MenuItemOption.Builder option =
        MenuItemOption.newBuilder()
          .setMenuItemOptionId("breadstick-sm")
          .setValue(
              MenuItemOptionProperty.newBuilder()
                .setPropertyType(MenuItemOptionProperty.PropertyType.SIZE)
                .setTextVal(TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Small")
                            .setLanguageCode("en-us"))))
          .setOfferSet(
              OfferSet.newBuilder()
                .addOffers(
                    Offer.newBuilder()
                      .setPrice(
                          Money.newBuilder()
                            .setCurrencyCode("USD")
                            .setUnits(8)
                            .setNanos(0))));

    FoodMenuFeed.Builder foodMenuFeed =
        FoodMenuFeed.newBuilder()
            .addData(MenuComponent.newBuilder().setMenu(menu))
            .addData(MenuComponent.newBuilder().setSection(section))
            .addData(MenuComponent.newBuilder().setItem(item))
            .addData(MenuComponent.newBuilder().setOption(option));

    // Example testing for menu feed size
    // Protocol buffer message must be less than 2 GiB
    // https://protobuf.dev/programming-guides/proto-limits/
    // It is recommended to not exceed 200 MB, as there is an Actions
    // Center limit of 200 MB per file after compression.
    int size = foodMenuFeed.build().getSerializedSize();
    if (size > MAX_BYTES_DATA_FILE) {
        // create new file
    }

    String feedJSON =
        JsonFormat.printer()
            .omittingInsignificantWhitespace()
            .preservingProtoFieldNames()
            .print(foodMenuFeed);

    System.out.println(feedJSON);
  }
}

    

এই কোড উদাহরণটিতে দেখানো হয়েছে কীভাবে নিম্নলিখিত প্রতিটি এনটিটির একটি করে নিয়ে একটি ফিড অবজেক্ট তৈরি করতে হয়:

  • মেনু
  • মেনু বিভাগ
  • মেনু আইটেম
  • মেনু আইটেম বিকল্প

এরপর উদাহরণটিতে দেখানো হয়েছে কীভাবে ফিডটিকে JSON-এ সিরিয়ালাইজ করতে হয়।

যান

  1. গো-এর জন্য প্রোটোক প্লাগইন ইনস্টল করুন
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        

    protoc-gen-go প্লাগইনটি অন্তর্ভুক্ত করতে PATH এনভায়রনমেন্ট ভেরিয়েবলটি আপডেট করুন, উদাহরণস্বরূপ:

    export PATH="$PATH:$(go env GOPATH)/bin"
        
  2. অ্যাপটি চালু করুন এবং ক্লায়েন্ট সোর্স কোড তৈরি করুন।
    go mod init feed/app
    mkdir generated
    protoc --go_out=./generated/ food_menu.proto money.proto localized_text.proto
        

    আপনি যদি Bazel ব্যবহার করেন, তাহলে protoc চালানোর বিকল্প হিসেবে go_proto_library রুলটি ব্যবহার করে দেখতে পারেন।

ব্যবহারের উদাহরণ

সম্পূর্ণ নমুনা প্রকল্পটি এখানে পাওয়া যাবে।

/*
Menu feed example used in
https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#go.
*
*/
package main

import (
	pb "feed/app/generated/food/menu/v1/proto"
	"fmt"

	localized_text "google.golang.org/genproto/googleapis/type/localized_text"
	money "google.golang.org/genproto/googleapis/type/money"
	"google.golang.org/protobuf/encoding/protojson"
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
	proto "google.golang.org/protobuf/proto"
)

func main() {

	//create a menu component
	menu := &pb.Menu{
		MenuId:      "menu1",
		MerchantIds: []string{"dining-1"},
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Menu",
				LanguageCode: "en-us",
			}},
		},
		Language: "en-us",
		LastMerchantUpdateTime: &timestamppb.Timestamp{
			Seconds: 1633621547,
		},
	}

	//create a menu section component
	section := &pb.MenuSection{
		MenuSectionId: "appetizers",
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Lunch Appetizers",
				LanguageCode: "en-us",
			}},
		},
		MenuItemIds: []string{"breadsticks-sauce"},
	}

	//create a menu item component
	item := &pb.MenuItem{
		MenuItemId: "breadsticks-sauce",
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Breadsticks & Sauce",
				LanguageCode: "en-us",
			}},
		},
		Description: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Breakfast basket w/ side of tomato sauce (size 6 or 12)",
				LanguageCode: "en-us",
			}},
		},
		Pricing: &pb.MenuItem_MenuItemOptionSet_{
			MenuItemOptionSet: &pb.MenuItem_MenuItemOptionSet{
				MenuItemOptionIds: []string{"breadstick-sm", "breadstick-lg"},
			},
		},
	}
	imageUris := []string{
		"http://www.example.com/photos/breadsticks.jpg",
		"http://www.example.com/photos/sauce.jpg",
	}
	for _, uri := range imageUris {
		image := &pb.Image{
			Uri: uri,
		}
		item.Images = append(item.Images, image)
	}

	//create a menu item option
	option := &pb.MenuItemOption{
		MenuItemOptionId: "breadstick-sm",
		Value: &pb.MenuItemOptionProperty{
			PropertyType: pb.MenuItemOptionProperty_SIZE,
			Value: &pb.MenuItemOptionProperty_TextVal{
				TextVal: &pb.TextField{
					Text: []*localized_text.LocalizedText{{
						Text:         "Small",
						LanguageCode: "en-us",
					}},
				},
			},
		},
		OfferSet: &pb.OfferSet{
			Offers: []*pb.Offer{{
				Price: &money.Money{
					CurrencyCode: "USD",
					Units:        8,
					Nanos:        0,
				},
			}},
		},
	}

	//create feed
	feed := &pb.FoodMenuFeed{
		Data: []*pb.MenuComponent{{
			Type: &pb.MenuComponent_Menu{
				Menu: menu,
			},
		},
			{
				Type: &pb.MenuComponent_Section{
					Section: section,
				},
			},
			{
				Type: &pb.MenuComponent_Item{
					Item: item,
				},
			},
			{
				Type: &pb.MenuComponent_Option{
					Option: option,
				},
			}},
	}

	// Example testing for menu feed size
    // Protocol buffer message must be less than 2 GiB
    // https://protobuf.dev/programming-guides/proto-limits/
    // It is recommended to not exceed 200 MB, as there is an Actions
    // Center limit of 200 MB per file after compression.
	if proto.Size(feed) > 200 * 1024 * 1024 {
		// create new file
	}

	marshalOptions := protojson.MarshalOptions{
		UseProtoNames: true,
	}
	jsonBytes, _ := marshalOptions.Marshal(feed)
	fmt.Printf("message = %s", string(jsonBytes))
}


    

এই কোড উদাহরণটিতে দেখানো হয়েছে কীভাবে নিম্নলিখিত প্রতিটি এনটিটির একটি করে নিয়ে একটি ফিড অবজেক্ট তৈরি করতে হয়:

  • মেনু
  • মেনু বিভাগ
  • মেনু আইটেম
  • মেনু আইটেম বিকল্প

এরপর উদাহরণটিতে দেখানো হয়েছে কীভাবে ফিডটিকে JSON-এ সিরিয়ালাইজ করতে হয়।