Skip to content
This repository was archived by the owner on Mar 11, 2022. It is now read-only.

Commit b509fcb

Browse files
authored
Fix long_description issue #34 (#36)
* Fix #34
1 parent 5f84d6d commit b509fcb

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

Description.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Project description
2+
3+
Cloudstate is a specification, protocol, and reference implementation for providing distributed state management patterns suitable for **Serverless** computing.
4+
The current supported and envisioned patterns include:
5+
6+
* **Event Sourcing**
7+
* **Conflict-Free Replicated Data Types (CRDTs)**
8+
* **Key-Value storage**
9+
* **P2P messaging**
10+
* **CQRS read side projections**
11+
12+
Cloudstate is polyglot, which means that services can be written in any language that supports gRPC,
13+
and with language specific libraries provided that allow idiomatic use of the patterns in each language.
14+
Cloudstate can be used either by itself, in combination with a Service Mesh,
15+
or it is envisioned that it will be integrated with other Serverless technologies such as [Knative](https://knative.dev/).
16+
17+
Read more about the design, architecture, techniques, and technologies behind Cloudstate in [this section in the documentation](https://github.com/cloudstateio/cloudstate/blob/master/README.md#enter-cloudstate).
18+
19+
The Cloudstate Python user language support is a library that implements the Cloudstate protocol and offers an pythonistic API
20+
for writing entities that implement the types supported by the Cloudstate protocol.
21+
22+
The Cloudstate documentation can be found [here](https://cloudstate.io/docs/)
23+
24+
## Install and update using pip:
25+
26+
```
27+
pip install -U cloudstate
28+
```
29+
30+
## A Simple EventSourced Example:
31+
32+
### 1. Define your gRPC contract
33+
34+
```
35+
// This is the public API offered by the shopping cart entity.
36+
syntax = "proto3";
37+
38+
import "google/protobuf/empty.proto";
39+
import "cloudstate/entity_key.proto";
40+
import "google/api/annotations.proto";
41+
import "google/api/http.proto";
42+
43+
package com.example.shoppingcart;
44+
45+
message AddLineItem {
46+
string user_id = 1 [(.cloudstate.entity_key) = true];
47+
string product_id = 2;
48+
string name = 3;
49+
int32 quantity = 4;
50+
}
51+
52+
message RemoveLineItem {
53+
string user_id = 1 [(.cloudstate.entity_key) = true];
54+
string product_id = 2;
55+
}
56+
57+
message GetShoppingCart {
58+
string user_id = 1 [(.cloudstate.entity_key) = true];
59+
}
60+
61+
message LineItem {
62+
string product_id = 1;
63+
string name = 2;
64+
int32 quantity = 3;
65+
}
66+
67+
message Cart {
68+
repeated LineItem items = 1;
69+
}
70+
71+
service ShoppingCart {
72+
rpc AddItem(AddLineItem) returns (google.protobuf.Empty) {
73+
option (google.api.http) = {
74+
post: "/cart/{user_id}/items/add",
75+
body: "*",
76+
};
77+
}
78+
79+
rpc RemoveItem(RemoveLineItem) returns (google.protobuf.Empty) {
80+
option (google.api.http).post = "/cart/{user_id}/items/{product_id}/remove";
81+
}
82+
83+
rpc GetCart(GetShoppingCart) returns (Cart) {
84+
option (google.api.http) = {
85+
get: "/carts/{user_id}",
86+
additional_bindings: {
87+
get: "/carts/{user_id}/items",
88+
response_body: "items"
89+
}
90+
};
91+
}
92+
}
93+
94+
```
95+
96+
### 2. Generate Python files
97+
98+
It is necessary to compile your .proto files using the protoc compiler in order to generate Python files.
99+
See [this official gRPC for Python quickstart](https://grpc.io/docs/languages/python/quickstart/) if you are not familiar with the gRPC protocol.
100+
101+
Here is an example of how to compile the sample proto file:
102+
```
103+
python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/shoppingcart.proto
104+
```
105+
106+
### 3. Implement your business logic under an EventSourced Cloudstate Entity
107+
108+
```
109+
from dataclasses import dataclass, field
110+
from typing import MutableMapping
111+
112+
from google.protobuf.empty_pb2 import Empty
113+
114+
from cloudstate.event_sourced_context import EventSourcedCommandContext
115+
from cloudstate.event_sourced_entity import EventSourcedEntity
116+
from shoppingcart.domain_pb2 import (Cart as DomainCart, LineItem as DomainLineItem, ItemAdded, ItemRemoved)
117+
from shoppingcart.shoppingcart_pb2 import (Cart, LineItem, AddLineItem, RemoveLineItem)
118+
from shoppingcart.shoppingcart_pb2 import (_SHOPPINGCART, DESCRIPTOR as FILE_DESCRIPTOR)
119+
120+
121+
@dataclass
122+
class ShoppingCartState:
123+
entity_id: str
124+
cart: MutableMapping[str, LineItem] = field(default_factory=dict)
125+
126+
127+
def init(entity_id: str) -> ShoppingCartState:
128+
return ShoppingCartState(entity_id)
129+
130+
131+
entity = EventSourcedEntity(_SHOPPINGCART, [FILE_DESCRIPTOR], init)
132+
133+
134+
def to_domain_line_item(item):
135+
domain_item = DomainLineItem()
136+
domain_item.productId = item.product_id
137+
domain_item.name = item.name
138+
domain_item.quantity = item.quantity
139+
return domain_item
140+
141+
142+
@entity.snapshot()
143+
def snapshot(state: ShoppingCartState):
144+
cart = DomainCart()
145+
cart.items = [to_domain_line_item(item) for item in state.cart.values()]
146+
return cart
147+
148+
149+
def to_line_item(domain_item):
150+
item = LineItem()
151+
item.product_id = domain_item.productId
152+
item.name = domain_item.name
153+
item.quantity = domain_item.quantity
154+
return item
155+
156+
157+
@entity.snapshot_handler()
158+
def handle_snapshot(state: ShoppingCartState, domain_cart: DomainCart):
159+
state.cart = {domain_item.productId: to_line_item(domain_item) for domain_item in domain_cart.items}
160+
161+
162+
@entity.event_handler(ItemAdded)
163+
def item_added(state: ShoppingCartState, event: ItemAdded):
164+
cart = state.cart
165+
if event.item.productId in cart:
166+
item = cart[event.item.productId]
167+
item.quantity = item.quantity + event.item.quantity
168+
else:
169+
item = to_line_item(event.item)
170+
cart[item.product_id] = item
171+
172+
173+
@entity.event_handler(ItemRemoved)
174+
def item_removed(state: ShoppingCartState, event: ItemRemoved):
175+
del state.cart[event.productId]
176+
177+
178+
@entity.command_handler("GetCart")
179+
def get_cart(state: ShoppingCartState):
180+
cart = Cart()
181+
cart.items.extend(state.cart.values())
182+
return cart
183+
184+
185+
@entity.command_handler("AddItem")
186+
def add_item(item: AddLineItem, ctx: EventSourcedCommandContext):
187+
if item.quantity <= 0:
188+
ctx.fail("Cannot add negative quantity of to item {}".format(item.productId))
189+
else:
190+
item_added_event = ItemAdded()
191+
item_added_event.item.CopyFrom(to_domain_line_item(item))
192+
ctx.emit(item_added_event)
193+
return Empty()
194+
195+
196+
@entity.command_handler("RemoveItem")
197+
def remove_item(state: ShoppingCartState, item: RemoveLineItem, ctx: EventSourcedCommandContext):
198+
cart = state.cart
199+
if item.product_id not in cart:
200+
ctx.fail("Cannot remove item {} because it is not in the cart.".format(item.productId))
201+
else:
202+
item_removed_event = ItemRemoved()
203+
item_removed_event.productId = item.product_id
204+
ctx.emit(item_removed_event)
205+
return Empty()
206+
```
207+
208+
### 4. Register Entity
209+
210+
```
211+
from cloudstate.cloudstate import CloudState
212+
from shoppingcart.shopping_cart_entity import entity as shopping_cart_entity
213+
import logging
214+
215+
if __name__ == '__main__':
216+
logging.basicConfig()
217+
CloudState().register_event_sourced_entity(shopping_cart_entity).start()
218+
```
219+
220+
### 5. Deployment
221+
222+
Cloudstate runs on Docker and Kubernetes you need to package your application so that it works as a Docker container
223+
and can deploy it together with Cloudstate Operator on Kubernetes, the details and examples of all of which can be found [here](https://code.visualstudio.com/docs/containers/quickstart-python), [here](https://github.com/cloudstateio/python-support/blob/master/shoppingcart/Dockerfile) and [here](https://cloudstate.io/docs/core/current/user/deployment/index.html).
224+
225+
## Contributing
226+
227+
For guidance on setting up a development environment and how to make a contribution to Cloudastate,
228+
see the contributing [project page](https://github.com/cloudstateio/python-support) or consult an official documentation [here](https://cloudstate.io/docs/).
229+
230+
## Links
231+
232+
* [Website:](https://cloudstate.io/)
233+
* [Documentation:](https://cloudstate.io/docs/)
234+
* [Releases:](https://pypi.org/project/cloudstate/)
235+
* [Code:](https://github.com/cloudstateio/python-support)
236+
* [Issue tracker:](https://github.com/cloudstateio/python-support/issues)

README.md renamed to Readme.md

File renamed without changes.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
license='Apache 2.0',
1919
description='Cloudstate Python Support Library',
2020
packages=find_packages(exclude=['tests', 'shoppingcart']),
21-
long_description=open('README.md', 'r').read(),
21+
long_description=open('Description.md', 'r').read(),
2222
long_description_content_type='text/markdown',
2323
zip_safe=False)

0 commit comments

Comments
 (0)