https://github.com/sleipnir/cloudstate-dart-support
Cloudstate Dart Language Support (This repository has been moved to https://github.com/cloudstateio/dart-support)
https://github.com/sleipnir/cloudstate-dart-support
Last synced: 10 months ago
JSON representation
Cloudstate Dart Language Support (This repository has been moved to https://github.com/cloudstateio/dart-support)
- Host: GitHub
- URL: https://github.com/sleipnir/cloudstate-dart-support
- Owner: sleipnir
- License: apache-2.0
- Created: 2020-04-09T05:30:44.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-04-17T20:58:51.000Z (about 6 years ago)
- Last Synced: 2024-10-29T11:12:52.431Z (over 1 year ago)
- Language: Dart
- Homepage: https://cloudstate.io/
- Size: 1.25 MB
- Stars: 5
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
Cloudstate Dart library
## Usage
A simple usage example:
Create new Dart project.
Add dependencies in pubspec.yml:
```yaml
name: shopping_cart
version: 0.5.1
description: A Cloudstate Dart ShoppingCart Example.
author: Adriano Santos
environment:
sdk: '>=2.7.0 <3.0.0'
dependencies:
cloudstate: ^0.5.1
dev_dependencies:
pedantic: ^1.8.0
build_runner: ^1.5.2
build_test: ^0.10.8
build_web_compilers: ^2.1.1
mockito: ^4.1.0
test: ^1.6.4
```
Write Protofiles:
```proto
// File => protos/shoppingcart.proto
// This is the public API offered by the shopping cart entity.
syntax = "proto3";
package com.example.shoppingcart;
import "google/protobuf/empty.proto";
import "cloudstate/entity_key.proto";
import "cloudstate/eventing.proto";
import "google/api/annotations.proto";
import "google/api/http.proto";
import "google/api/httpbody.proto";
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: "/cart/{user_id}/items/add",
body: "*",
};
option (.cloudstate.eventing).in = "items";
}
rpc RemoveItem(RemoveLineItem) returns (google.protobuf.Empty) {
option (google.api.http).post = "/cart/{user_id}/items/{product_id}/remove";
}
rpc GetCart(GetShoppingCart) returns (Cart) {
option (google.api.http) = {
get: "/carts/{user_id}",
additional_bindings: {
get: "/carts/{user_id}/items",
response_body: "items"
}
};
}
}
// File => protos/persistence/domain.proto
// These are the messages that get persisted - the events, plus the current state (Cart) for snapshots.
syntax = "proto3";
package com.example.shoppingcart.persistence;
message LineItem {
string productId = 1;
string name = 2;
int32 quantity = 3;
}
// The item added event.
message ItemAdded {
LineItem item = 1;
}
// The item removed event.
message ItemRemoved {
string productId = 1;
}
// The shopping cart state.
message Cart {
repeated LineItem items = 1;
}
```
Compiling your proto files:
```shell script
[sleipnir@sleipnir example]# protoc --include_imports \
--descriptor_set_out=user-function.desc \
-I protos/persistence/domain.proto protos/shoppingcart.proto \
--dart_out=grpc:lib/src/generated
````
Write file => lib/eventsourced_entity.dart:
```dart
import 'package:cloudstate/cloudstate.dart';
import 'generated/google/protobuf/empty.pb.dart';
import 'generated/persistence/domain.pb.dart' as Domain;
import 'generated/shoppingcart.pb.dart' as Shoppingcart;
@EventSourcedEntity()
class ShoppingCartEntity {
final Map _cart = {};
String entityId;
Context context;
ShoppingCartEntity.create(@EntityId() String entityId, Context context) {
this.entityId = entityId;
this.context = context;
}
@Snapshot()
Domain.Cart snapshot() {
return Domain.Cart.create()
..items.addAll(
_cart.values.map((e) => convertShoppingItem(e))
.toList());
}
@SnapshotHandler()
void handleSnapshot(Domain.Cart cart) {
_cart.clear();
for (var item in cart.items) {
_cart[item.productId] = convert(item);
}
}
@EventHandler()
void itemAdded(Domain.ItemAdded itemAdded) {
var item = _cart[itemAdded.item.productId];
if (item == null) {
item = convert(itemAdded.item);
} else {
item =
item..quantity = item.quantity + itemAdded.item.quantity;
}
_cart[item.productId] = item;
}
@EventHandler()
void itemRemoved(Domain.ItemRemoved itemRemoved) {
_cart.remove(itemRemoved.productId);
}
@EventSourcedCommandHandler()
Shoppingcart.Cart getCart() {
return Shoppingcart.Cart.create()
..items.addAll(_cart.values);
}
@EventSourcedCommandHandler()
Empty addItem(Shoppingcart.AddLineItem item, CommandContext ctx) {
if (item.quantity <= 0) {
ctx.fail('Cannot add negative quantity of to item ${item.productId}');
}
var lineIem = Domain.LineItem.create()
..productId = item.productId
..name = item.name
..quantity = item.quantity;
ctx.emit(Domain.ItemAdded.create()..item = lineIem);
return Empty.getDefault();
}
@EventSourcedCommandHandler()
Empty removeItem(Shoppingcart.RemoveLineItem item, CommandContext ctx) {
if (!_cart.containsKey(item.productId)) {
ctx.fail('Cannot remove item ${item.productId} because it is not in the cart.');
}
ctx.emit(Domain.ItemRemoved.create()..productId = item.productId);
return Empty.getDefault();
}
Shoppingcart.LineItem convert(Domain.LineItem item) {
return Shoppingcart.LineItem.create()
..productId = item.productId
..name = item.name
..quantity = item.quantity;
}
Domain.LineItem convertShoppingItem(Shoppingcart.LineItem item) {
return Domain.LineItem.create()
..productId = item.productId
..name = item.name
..quantity = item.quantity;
}
}
```
***Note: You are not required to create a constructor, but it is useful if you want to access the entity id
or the EventSourcedCreatedContext. In this case, the class is only allowed to have only one constructor, which can be a
normal constructor or a Named Constructor. Don't forget to annotate the entity parameter with the @EntityId annotation***
Write file => bin/shopping_cart.dart:
```dart
import 'package:cloudstate/cloudstate.dart';
import 'package:shopping_cart/src/eventsourced_entity.dart';
void main() {
Cloudstate()
..port = 8080
..address = 'localhost'
..registerEventSourcedEntity('com.example.shoppingcart.ShoppingCart', ShoppingCartEntity)
..start();
}
```
Build and run on docker:
```
docker build -t sleipnir/cloudstate-dart-shoppingcart:0.5.1
docker run --rm -p 8080:8080 -p 8181:8181 --name dart-shoppingcartsleipnir/cloudstate-dart-shoppingcart:0.5.1
```