Create a proto file

You define message types in a .proto file along with a service that specifies the remote procedure calls.

I. Create the file

Start your .proto file:

  1. Create a cart.proto file in the shoppingcart directory.

  2. To specify the syntax version, add the following on the first line of the file:

    syntax = "proto3";
  3. The example uses Google and Cloudstate functionality. Add the following import statements:

    import "google/protobuf/empty.proto";
    import "google/api/annotations.proto";
    import "cloudstate/entity_key.proto";
    import "cloudstate/eventing.proto";
    • The google imports allow you to send empty responses and add annotations to expose the service with a REST interface.

    • The cloudstate imports add event sourcing and key capabilities.

  4. To prevent naming conflicts, add a package declaration:

    package com.example.shoppingcart;

II. Add message definitions

The messages you define in cart.proto are received by your service as requests and are sent back as responses. Message definitions are aggregates that contain a set of typed fields. Each Cloudstate Entity requires a key, which is defined by the annotation [(.cloudstate.entity_key) = true]. Cloudstate uses the entity key to know which instance of an entity a command is for.

  1. Add message definitions corresponding to the functionality needed by a shopping cart:

    message AddLineItem {
        string user_id = 1 [(.cloudstate.entity_key) = true];
        string product_id = 2;
        string name = 3;
        int32 quantity = 4;
    }
    
    message RemoveLineItem {
        string user_id = 1 [(.cloudstate.entity_key) = true];
        string product_id = 2;
    }
    
    message GetShoppingCart {
        string user_id = 1 [(.cloudstate.entity_key) = true];
    }
    
    message LineItem {
        string product_id = 1;
        string name = 2;
        int32 quantity = 3;
    }
    
    message Cart {
        repeated LineItem items = 1;
    }
In the above example, the message AddLineItem contains fields for, among others, user_id and product_id. The ` = 1`, ` = 2` at the end of the lines are the unique "tags" that field uses in the binary encoding.

III. Add the service

To use the messages with the entity implementation, add a ShoppingCart service, which will define the remote procedure calls for the messages.

  1. Add the ShoppingCart service as follows:

    service ShoppingCart {
    
    }
  2. In the ShoppingCart service, add a remote procedure call for AddItem:

    rpc AddItem(AddLineItem) returns (google.protobuf.Empty) {
        option (google.api.http) = {
            post: "/com.example.shoppingcart.ShoppingCart/cart/{user_id}/items/add",
            body: "*",
        };
        option (.cloudstate.eventing).in = "items";
    }
    The AddItem remote procedure call requires an AddLineItem message and responds with an empty message. The google.api.http option instructs Akka Serverless to expose this remote procedure call as a POST HTTP call with the above path.
  3. In the ShoppingCart service, add a remote procedure call for RemoveItem:

    rpc RemoveItem(RemoveLineItem) returns (google.protobuf.Empty) {
        option (google.api.http).post = "/com.example.shoppingcart.ShoppingCart/cart/{user_id}/items/{product_id}/remove";
    }
  4. In the ShoppingCart service, add a remote procedure call for GetCart:

    rpc GetCart(GetShoppingCart) returns (Cart) {
        option (google.api.http) = {
          get: "/com.example.shoppingcart.ShoppingCart/carts/{user_id}",
          additional_bindings: {
            get: "/carts/{user_id}/items",
            response_body: "items"
          }
        };
    }

The completed cart.proto file

Your complete cart.proto file should look like the following:

syntax = "proto3";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";
import "cloudstate/entity_key.proto";
import "cloudstate/eventing.proto";

package com.example.shoppingcart;

message AddLineItem {
    string user_id = 1 [(.cloudstate.entity_key) = true];
    string product_id = 2;
    string name = 3;
    int32 quantity = 4;
 }

message RemoveLineItem {
    string user_id = 1 [(.cloudstate.entity_key) = true];
    string product_id = 2;
}

message GetShoppingCart {
    string user_id = 1 [(.cloudstate.entity_key) = true];
}

message LineItem {
    string product_id = 1;
    string name = 2;
    int32 quantity = 3;
}

message Cart {
    repeated LineItem items = 1;
}
service ShoppingCart {
    rpc AddItem(AddLineItem) returns (google.protobuf.Empty) {
        option (google.api.http) = {
        post: "/com.example.shoppingcart.ShoppingCart/cart/{user_id}/items/add",
        body: "*",
    };
        option (.cloudstate.eventing).in = "items";
}

    rpc RemoveItem(RemoveLineItem) returns (google.protobuf.Empty) {
        option (google.api.http).post =
            "/com.example.shoppingcart.ShoppingCart/cart/{user_id}/items/{product_id}/remove";
    }

    rpc GetCart(GetShoppingCart) returns (Cart) {
        option (google.api.http) = {
        get: "/com.example.shoppingcart.ShoppingCart/carts/{user_id}",
        additional_bindings: {
        get: "/carts/{user_id}/items",
        response_body: "items"
        }
    };
    }
}