cloudstate 0.5.8

  • Readme
  • Changelog
  • Installing
  • 48

Cloudstate Dart library

Usage #

A simple usage example:

Create new Dart project.

Add dependencies in pubspec.yml:

name: shopping_cart
version: 0.5.1
description: A Cloudstate Dart ShoppingCart Example.
author: Adriano Santos <sleipnir@bsd.com.br>

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:

// 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:

[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:


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<String, Shoppingcart.LineItem> _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);
  }

  @CommandHandler()
  Shoppingcart.Cart getCart() {
    return Shoppingcart.Cart.create()
        ..items.addAll(_cart.values);
  }

  @CommandHandler()
  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();
  }

  @CommandHandler()
  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:


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

0.5.8 #

  • Fix Cloudstate documentation

0.5.7 #

  • New Cloudstate documentation

0.5.6 #

  • Add TCK tests

0.5.5 #

  • Fix. DiscoveryService version

0.5.0 #

  • Initial version, created by Stagehand

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  cloudstate: ^0.5.8

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:cloudstate/cloudstate.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
26
Health:
Code health derived from static analysis. [more]
75
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
60
Overall:
Weighted score of the above. [more]
48
Learn more about scoring.

We analyzed this package on Jul 11, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.15

Analysis suggestions

Package not compatible with SDK flutter

Because it is not compatible with any of the supported runtimes: flutter-native, flutter-web

Package not compatible with runtime flutter-native on Android

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on Linux

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on Windows

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on iOS

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on macOS

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-web on Web

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:logger/logger.dart that imports:
  • package:logger/src/printers/simple_printer.dart that imports:
  • package:io/ansi.dart that imports:
  • package:io/src/ansi_code.dart that imports:
  • dart:io

Package not compatible with runtime js

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:logger/logger.dart that imports:
  • package:logger/src/printers/simple_printer.dart that imports:
  • package:io/ansi.dart that imports:
  • package:io/src/ansi_code.dart that imports:
  • dart:io

Package not compatible with runtime native-aot

Because:

  • package:cloudstate/cloudstate.dart that imports:
  • package:cloudstate/src/services.dart that imports:
  • package:cloudstate/src/stateless.dart that imports:
  • dart:mirrors

Health suggestions

Fix lib/src/generated/protocol/cloudstate/crdt.pbjson.dart. (-25 points)

Analysis of lib/src/generated/protocol/cloudstate/crdt.pbjson.dart reported 154 hints, including:

line 8 col 35: Avoid const keyword.

line 10 col 8: Avoid const keyword.

line 11 col 5: Avoid const keyword.

line 12 col 5: Avoid const keyword.

line 13 col 5: Avoid const keyword.

Fix lib/src/generated/protocol/cloudstate/entity.pbjson.dart. (-25 points)

Analysis of lib/src/generated/protocol/cloudstate/entity.pbjson.dart reported 63 hints, including:

line 8 col 20: Avoid const keyword.

line 10 col 8: Avoid const keyword.

line 11 col 5: Avoid const keyword.

line 22 col 22: Avoid const keyword.

line 24 col 8: Avoid const keyword.

Fix lib/src/generated/protocol/google/protobuf/descriptor.pbjson.dart. (-25 points)

Analysis of lib/src/generated/protocol/google/protobuf/descriptor.pbjson.dart reported 264 hints, including:

line 8 col 32: Avoid const keyword.

line 10 col 8: Avoid const keyword.

line 11 col 5: Avoid const keyword.

line 22 col 34: Avoid const keyword.

line 24 col 8: Avoid const keyword.

Fix additional 10 files with analysis or formatting issues. (-27.04 points)

Additional issues in the following files:

  • lib/src/generated/protocol/cloudstate/event_sourced.pbjson.dart (33 hints)
  • lib/src/generated/protocol/cloudstate/function.pbjson.dart (13 hints)
  • lib/src/generated/protocol/google/protobuf/any.pbjson.dart (4 hints)
  • lib/src/services.dart (4 hints)
  • lib/src/generated/protocol/google/protobuf/empty.pbjson.dart (1 hint)
  • lib/src/reflect_helper.dart (1 hint)
  • lib/src/stateless.dart (1 hint)
  • lib/cloudstate.dart (Run dartfmt to format lib/cloudstate.dart.)
  • lib/src/base.dart (Run dartfmt to format lib/src/base.dart.)
  • lib/src/event_sourced.dart (Run dartfmt to format lib/src/event_sourced.dart.)

Maintenance issues and suggestions

Support latest dependencies. (-20 points)

The version constraint in pubspec.yaml does not support the latest published versions for 2 dependencies (logger, optional).

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Maintain an example.

None of the files in the package's example/ directory matches known example patterns.

Common filename patterns include main.dart, example.dart, and cloudstate.dart. Packages with multiple examples should provide example/README.md.

For more information see the pub package layout conventions.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
async ^2.2.0 2.4.2
fixnum ^0.10.11 0.10.11
googleapis_auth ^0.2.7 0.2.12
grpc ^2.1.3 2.2.0
http ^0.12.0 0.12.1
http2 ^1.0.0 1.0.0
logger ^0.8.3 0.8.3 0.9.1
meta ^1.1.6 1.2.2 1.3.0-nullsafety
optional ^4.1.0 4.2.0 5.0.0
path ^1.7.0 1.7.0
protobuf ^1.0.1 1.0.1
yaml ^2.2.0 2.2.1
Transitive dependencies
charcode 1.1.3
collection 1.14.13 1.15.0-nullsafety
convert 2.1.1
crypto 2.1.5
http_parser 3.1.4
io 0.3.4
source_span 1.7.0
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.2.0 1.3.0-nullsafety
Dev dependencies
build_runner ^1.5.2
build_test ^0.10.8
mockito ^4.1.0
pedantic ^1.8.0 1.9.2
test ^1.6.4