Skip to content

Commit

Permalink
Rel. 0.5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
massenz committed Sep 2, 2022
2 parents 121b756 + 2f4b1cc commit 6027b64
Show file tree
Hide file tree
Showing 26 changed files with 591 additions and 794 deletions.
6 changes: 3 additions & 3 deletions .run/Run SQS Client.run.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run SQS Client" type="GoApplicationRunConfiguration" factoryName="Go Application">
<configuration default="false" name="Run SQS Client" type="GoApplicationRunConfiguration" factoryName="Go Application" editBeforeRun="true">
<module name="statemachine" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="-endpoint http://localhost:4566 -q events -dest 4deabf91-4da3-4813-842b-d01d13d4dfa6 -evt accept" />
<parameters value="-endpoint http://localhost:4566 -q events -dest 674ab2cf-f8e3-40c2-95d7-1eaa0cec7bf4 -evt sign" />
<kind value="FILE" />
<package value="github.com/massenz/go-statemachine/clients" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/clients/sqs_client.go" />
<filePath value="$PROJECT_DIR$/clients/sqs_client.go|$PROJECT_DIR$/clients/orders.go" />
<method v="2" />
</configuration>
</component>
22 changes: 2 additions & 20 deletions .run/Run Server.run.xml
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
<!--
~ Copyright (c) 2022 AlertAvert.com. All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ Author: Marco Massenzio ([email protected])
-->

<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Server" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="statemachine" />
Expand All @@ -28,7 +10,7 @@
<kind value="FILE" />
<package value="github.com/massenz/go-statemachine/cmd" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/cmd/server.go" />
<filePath value="$PROJECT_DIR$/cmd/main.go" />
<method v="2" />
</configuration>
</component>
</component>
24 changes: 3 additions & 21 deletions .run/Run gRPC Client.run.xml
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
<!--
~ Copyright (c) 2022 AlertAvert.com. All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ Author: Marco Massenzio ([email protected])
-->

<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run gRPC Client" type="GoApplicationRunConfiguration" factoryName="Go Application" editBeforeRun="true">
<module name="statemachine" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="-addr :7398 -dest 3 -evt ship" />
<parameters value="-addr :7398 -dest 674ab2cf-f8e3-40c2-95d7-1eaa0cec7bf4 -evt ship" />
<kind value="FILE" />
<package value="github.com/massenz/go-statemachine/clients" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/clients/grpc_client.go" />
<filePath value="$PROJECT_DIR$/clients/grpc_client.go|$PROJECT_DIR$/clients/orders.go" />
<method v="2" />
</configuration>
</component>
</component>
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ build: cmd/main.go
go build -ldflags "-X main.Release=$(tag)" -o $(out) cmd/main.go
@chmod +x $(out)

$(out): build

services:
@docker-compose -f $(compose) up -d

Expand All @@ -22,10 +24,10 @@ queues:
aws --no-cli-pager --endpoint-url=http://localhost:4566 --region us-west-2 \
sqs create-queue --queue-name $$queue; done >/dev/null

test: build services queues
test: $(out) services queues
ginkgo -p $(pkgs)

container:
container: $(out)
docker build -f $(dockerfile) -t $(image):$(tag) .

# Runs test coverage and displays the results in browser
Expand All @@ -34,5 +36,6 @@ cov: build services queues
@go tool cover -html=/tmp/cov.out

clean:
@rm -f api/*.pb.go $(out)
@rm $(out)
@docker-compose -f $(compose) down
@docker rmi $(image):$(tag)
60 changes: 38 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,35 +211,51 @@ GET /api/v1/configurations/test.orders:v3
### SQS Messages

> **NOTE**<br/>
> **The format of the `Body` of the message will change, to include a `details` string**
#### EventRequest

To send an Event to an FSM via an SQS Message we use the [following code](clients/sqs_client.go):

```golang
_, err = queue.SendMessage(&sqs.SendMessageInput{
MessageAttributes: map[string]*sqs.MessageAttributeValue{
"DestinationId": {
DataType: aws.String("String"),
StringValue: aws.String("6b5af0e8-9033-47e2-97db-337476f1402a"),
},
"EventId": {
DataType: aws.String("String"),
StringValue: aws.String(uuid.NewString()),
},
"Sender": {
DataType: aws.String("String"),
StringValue: aws.String("SQS Client"),
},
},
MessageBody: aws.String("backorder"),
QueueUrl: queueUrl.QueueUrl,
})
// This is the object you want to send across as Event's metadata.
order := NewOrderDetails(uuid.NewString(), "sqs-cust-1234", 99.99)

msg := &protos.EventRequest{
Event: &protos.Event{
// This is actually unnecessary; if no EventId is present, SM will
// generate one automatically and if the client does not need to store
// it somewhere else, it is safe to omit it.
EventId: uuid.NewString(),

// This is also unnecessary, as SM will automatically generate a timestamp
// if one is not already present.
Timestamp: timestamppb.Now(),

Transition: &protos.Transition{Event: "backorder"},
Originator: "New SQS Client with Details",

// Here you convert the Event metadata to a string by, e.g., JSON-serializing it.
Details: order.String(),
},

// This is the unique ID for the entity you are sending the event to; MUST
// match the `id` of an existing `statemachine` (see the REST API).
Dest: "6b5af0e8-9033-47e2-97db-337476f1402a",
}

_, err = queue.SendMessage(&sqs.SendMessageInput{
// Here we serialize the Protobuf using text serialization.
MessageBody: aws.String(proto.MarshalTextString(msg)),
QueueUrl: queueUrl.QueueUrl,
})
```

This will cause a `backorder` event to be sent to our FSM whose `id` matches the UUID in `dest`; if there are errors (eg, the FSM does not exist, or the event is not allowed for the machine's configuration and current state) errors may be optionally sent to the SQS queue configured via the `-errors` option (see [Running the Server](#running-the-server)): see the [`pubsub` code](pubsub/sqs_pub.go) code for details as to how we encode the error message as an SQS message.
This will cause a `backorder` event to be sent to our FSM whose `id` matches the UUID in `Dest`; if there are errors (eg, the FSM does not exist, or the event is not allowed for the machine's configuration and current state) errors may be optionally sent to the SQS queue configured via the `-errors` option (see [Running the Server](#running-the-server)): see the [`pubsub` code](pubsub/sqs_pub.go) code for details as to how we encode the error message as an SQS message.

See [`EventRequest` in `statemachine-proto`](https://github.com/massenz/statemachine-proto/blob/main/api/statemachine.proto#L86) for details on the event being sent.

#### SQS Error notifications

`TODO: add encoding description of the notification message`
`TODO:` Once we refactor `EventErrorMessage` we should update this section too.


### gRPC Methods
Expand Down
52 changes: 9 additions & 43 deletions api/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,14 @@ type ConfiguredStateMachine struct {
}

func NewStateMachine(configuration *protos.Configuration) (*ConfiguredStateMachine, error) {
if configuration.Name == "" {
if configuration.Name == "" || configuration.Version == "" {
Logger.Error("Missing configuration name")
return nil, MalformedConfigurationError
}
if configuration.Version == "" {
configuration.Version = "v1"
}
return &ConfiguredStateMachine{
FSM: &protos.FiniteStateMachine{
ConfigId: configuration.Name + ":" + configuration.Version,
State: configuration.StartingState,
//History: make([]string, 0),
},
Config: configuration,
}, nil
Expand Down Expand Up @@ -189,42 +185,12 @@ func CheckValid(c *protos.Configuration) error {
return nil
}

//////// encoding interface /////////////

type MyConfig struct {
protos.Configuration
}

type MyFSM struct {
protos.FiniteStateMachine
}

// MarshalBinary is needed to encode the data before storing in Redis,
// and to retrieve it later.
//
// **NOTE** the receiver must be a concrete type (NOT a pointer) or the
// serialization to Redis will fail.
func (x MyConfig) MarshalBinary() ([]byte, error) {
return proto.Marshal(&x)
}

// UnmarshalBinary is the dual of MarshalBinary and will parse the
// binary data into the receiver.
// See: https://pkg.go.dev/encoding
func (x *MyConfig) UnmarshalBinary(data []byte) error {
res := proto.Unmarshal(data, x)
return res
}

// Identical implementation for the FiniteStateMachine, but necessary as
// we can't really define an ABC for both types, and using proto.Message wouldn't
// work either.

func (x MyFSM) MarshalBinary() ([]byte, error) {
return proto.Marshal(&x)
}

func (x *MyFSM) UnmarshalBinary(data []byte) error {
res := proto.Unmarshal(data, x)
return res
// UpdateEvent adds the ID and timestamp to the event, if not already set.
func UpdateEvent(event *protos.Event) {
if event.EventId == "" {
event.EventId = uuid.NewString()
}
if event.Timestamp == nil {
event.Timestamp = tspb.Now()
}
}
12 changes: 6 additions & 6 deletions api/statemachine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ var _ = Describe("FSM Protocol Buffers", func() {
})

Describe("Finite State Machines", func() {
Context("with an unnamed configuration", func() {
Context("with a configuration", func() {
var spaceship protos.Configuration

BeforeEach(func() {
Expand All @@ -125,18 +125,18 @@ var _ = Describe("FSM Protocol Buffers", func() {
})

It("without name will fail", func() {
spaceship.Version = "v0.1"
_, err := NewStateMachine(&spaceship)
Expect(err).Should(HaveOccurred())
})
It("will get a default version, if missing", func() {
It("will fail with a missing configuration version", func() {
spaceship.Name = "mars_orbiter"
s, err := NewStateMachine(&spaceship)
Expect(err).ShouldNot(HaveOccurred())
Expect(s.FSM.ConfigId).To(Equal("mars_orbiter:v1"))
_, err := NewStateMachine(&spaceship)
Expect(err).To(HaveOccurred())
})
It("will carry the configuration embedded", func() {
spaceship.Name = "mars_orbiter"
spaceship.Version = "v3"
spaceship.Version = "v1.0.1"
s, err := NewStateMachine(&spaceship)
Expect(err).ToNot(HaveOccurred())
Expect(s).ToNot(BeNil())
Expand Down
2 changes: 1 addition & 1 deletion build.settings
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Build configuration

version = 0.5.0
version = 0.5.1
5 changes: 4 additions & 1 deletion clients/grpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func main() {
cc, _ := grpc.Dial(*serverAddr, clientOptions...)
client := api.NewStatemachineServiceClient(cc)

// Fake order
order := NewOrderDetails(uuid.New().String(), "cust-1234", 123.55)
response, err := client.ConsumeEvent(context.Background(),
&api.EventRequest{
Event: &api.Event{
Expand All @@ -52,7 +54,8 @@ func main() {
Transition: &api.Transition{
Event: *event,
},
Originator: "gRPC Client",
Details: order.String(),
Originator: "new gRPC Client with details",
},
Dest: *fsmId,
})
Expand Down
48 changes: 48 additions & 0 deletions clients/orders.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2022 AlertAvert.com. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Marco Massenzio ([email protected])
*/

package main

import (
"encoding/json"
"time"
)

type OrderDetails struct {
OrderId string
CustomerId string
OrderDate time.Time
OrderTotal float64
}

func NewOrderDetails(orderId, customerId string, orderTotal float64) *OrderDetails {
return &OrderDetails{
OrderId: orderId,
CustomerId: customerId,
OrderDate: time.Now(),
OrderTotal: orderTotal,
}
}

func (o *OrderDetails) String() string {
res, error := json.Marshal(o)
if error != nil {
panic(error)
}
return string(res)
}
Loading

0 comments on commit 6027b64

Please sign in to comment.