Skip to content

Commit

Permalink
Merge pull request #34 from Axway/dev
Browse files Browse the repository at this point in the history
Release 0.0.11
  • Loading branch information
jcollins-axway authored Jul 31, 2019
2 parents c026bf5 + 16510f6 commit 916447d
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 121 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile-gosec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pipeline {
ln -s ${WORKSPACE} ace-golang-sdk
cd $GOPATH/src/github.com/Axway/ace-golang-sdk
dep ensure -v
gosec -severity medium -fmt=json -out=$GOPATH/src/github.com/Axway/ace-golang-sdk/gosec-results.json ./...
gosec -exclude=G104 -severity medium -fmt=json -out=$GOPATH/src/github.com/Axway/ace-golang-sdk/gosec-results.json ./...
'''
}
}
Expand Down
92 changes: 62 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
ACE SDK allows developers to implement microservices that can be used as executable step in a choreography.

# Service Implemenation and Using the SDK

## Implement the callback method

### The callback method signature

Implement the callback method that will process the received message. Below is the signature of the method

```
func businessMessageProcessor(ctx context.Context, businessMsg *messaging.BusinessMessage, msgProducer linker.MsgProducer) error
func businessMessageProcessor(ctx context.Context, businessMsg *messaging.BusinessMessage, msgProducer linker.MsgProducer) error
```

### Input processing
Expand All @@ -27,41 +29,49 @@ The payload [type messaging.Payload] can be actual payload (use messaging.Payloa
The metadata can be retrieved using the interface method GetMetaData() on businessMsg object which returns map of string key/value pairs identifying the metadata associated with the payload.

### Output processing

The output of the processing can be responded by generating new message(s). To create a new message construct business message and setup metadata. The new message can then be produced using clientRelay parameter.

#### Creating new ACE business message
- Construct the metadata

Constructing the metadata is done by setting up a map of string key/value pairs
- Construct the metadata

Constructing the metadata is done by setting up a map of string key/value pairs

- Contruct the payload

Create the payload with content as demonstrated below. The newContent in the example below is a byte array holding the raw content

- Contruct the payload
```
newPayload := messaging.Payload{Body: newContent}
```
Create the payload with content as demonstrated below. The newContent in the example below is a byte array holding the raw content
```
newPayload := messaging.Payload{Body: newContent}
```
OR
OR
Create the payload with location reference as demonstrated below. The newContent in the example below is a byte array holding the the location reference.
Create the payload with location reference as demonstrated below. The newContent in the example below is a byte array holding the the location reference.
```
newPayload := messaging.Payload{Body: newContent, LocationReference: true}
```
```
newPayload := messaging.Payload{Body: newContent, LocationReference: true}
```
- Construct the ACE business message
- Construct the ACE business message
Create new business message object as demonstrated below. The "newMetadata" and "newPayload" in the example below identifies the metadata and payload for the new business message
```
newBusinessMsg := messaging.BusinessMessage{ MetaData: newMetadata, Payload: &newPayload}
```
Create new business message object as demonstrated below. The "newMetadata" and "newPayload" in the example below identifies the metadata and payload for the new business message
```
newBusinessMsg := messaging.BusinessMessage{ MetaData: newMetadata, Payload: &newPayload}
```
#### Producing message
To produce messages use the Send method on msgProducer parameter as demostrated below
```
msgProducer.Send(&newBusinessMsg)
msgProducer.Send(&newBusinessMsg)
```
## Add trace for service execution (Optional)
ACE SDK has instrumentation for OpenTracing(https://opentracing.io/specification/) built-in and provides ability to allow the business service to inject the tracing spans.
To start a span as child of span managed by ACE, use StartSpanFromContext method from opentracing package. Using the created span the business service can log details as demonstrated below.
Expand All @@ -73,39 +83,61 @@ span.Finish()
```
## Handling errors in the service execution
ACE SDK had three error types defined.
SendingError - Can be returned from calling send method on the MsgProducer.
```
error := msgProducer.Send(&newBusinessMsg)
error := msgProducer.Send(&newBusinessMsg)
```
ProcessingError - Returned by the BusinessMessageProcessor.
```
return linker.NewProcessingError(fmt.Errorf("processing error)"))
return linker.NewProcessingError(fmt.Errorf("processing error)"))
```
SystemError - Returned by the BusinessMessageProcessor to clientRelay.
```
return linker.NewSystemError(fmt.Errorf("system error)"))
return linker.NewSystemError(fmt.Errorf("system error)"))
```
## Register the service callback method with ACE
ACE business service must register the service info and callback method for making it usable as a step to build choreographies
The service registration needs following details
- Service Name
- Service Version
- Service Description
- Callback method
- Service Name
- Service Version
- Service Description
- Callback method
The business service may also define config parameters to be used when using the service in a choreography. These
parameters need to be added prior to registering the callback method
```
# String Parmeters (name, default value, isRequired)
AddStringConfigParam("parameterName", "defaultValue", true)

# Integer Parmeters (name, default value, isRequired)
AddIntConfigParam("parameterName", 123, false)

# Boolean Parmeters (name, default value)
AddBooleanConfigParam("parameterName", false)
```
Below is an example of Registering the service info & callback method and then starting the ACE processing
```
aceAgent, err := linker.Register(serviceName, serviceVersion, serviceDescription, businessMessageProcessor)
aceAgent, err := linker.Register(serviceName, serviceVersion, serviceDescription, serviceType, businessMessageProcessor)

aceAgent.Start()
```
The provided template reads the serviceName, serviceVersion and serviceDescription from following environment variables respectively, but its the implemenation choice on how to setup these details.
- SERVICE_NAME
- SERVICE_VERSION
- SERVICE_DESCRIPTION
- SERVICE_NAME
- SERVICE_VERSION
- SERVICE_DESCRIPTION
78 changes: 71 additions & 7 deletions linker/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package linker
import (
"context"
"fmt"
"strconv"

"go.uber.org/zap"

Expand Down Expand Up @@ -60,6 +61,7 @@ type Link struct {
var link *Link
var traceLogging tracing.TraceLogging
var log = logging.Logger()
var serviceConfigParamTemplates []*rpc.ConfigParameter

// MsgProducer - what is exposed to client business function
type MsgProducer interface {
Expand All @@ -69,6 +71,68 @@ type MsgProducer interface {
// BusinessMessageProcessor type of 'business' function used to process paylod relayed to Linker
type BusinessMessageProcessor func(context.Context, []*messaging.BusinessMessage, MsgProducer) error

// Add config parameter to list
func addConfigParam(configParam *rpc.ConfigParameter) {
serviceConfigParamTemplates = append(serviceConfigParamTemplates, configParam)
}

func checkDuplicateConfigParam(name string) error {
for _, cfgParam := range serviceConfigParamTemplates {
if cfgParam.Name == name {
return fmt.Errorf("Duplicate definition for config parameter : %s", name)
}
}
return nil
}

// AddStringConfigParam - Add String config parameter for the service
func AddStringConfigParam(name, defaultValue string, required bool) error {
err := checkDuplicateConfigParam(name)
if err != nil {
return err
}
stringConfigParam := &rpc.ConfigParameter{
Name: name,
Type: "string",
DefaultValue: defaultValue,
IsRequired: required,
}
addConfigParam(stringConfigParam)
return nil
}

// AddIntConfigParam - Add integer config parameter for the service
func AddIntConfigParam(name string, defaultValue int, required bool) error {
err := checkDuplicateConfigParam(name)
if err != nil {
return err
}
intConfigParam := &rpc.ConfigParameter{
Name: name,
Type: "int",
DefaultValue: strconv.Itoa(defaultValue),
IsRequired: required,
}
addConfigParam(intConfigParam)
return nil
}

// AddBooleanConfigParam - Add boolean config parameter for the service
func AddBooleanConfigParam(name string, defaultValue bool) error {
err := checkDuplicateConfigParam(name)
if err != nil {
return err
}
boolConfigParam := &rpc.ConfigParameter{
Name: name,
Type: "boolean",
DefaultValue: strconv.FormatBool(defaultValue),
IsRequired: true,
}
addConfigParam(boolConfigParam)
return nil
}

// Register -Registers the business service with linker
func Register(name, version, description, serviceType string, fn BusinessMessageProcessor) (*Link, error) {
if link != nil {
Expand Down Expand Up @@ -167,7 +231,6 @@ func (link Link) OnRelay(aceMessage *rpc.Message) {
panic(fmt.Sprintf("MsgProcessor of %s agent is not of expected BusinessMessageProcessor type, actual: %T\n",
link.name, msgProcessor))
}

}

// OnSidecarRegistrationComplete can perform Linker-specific post registration actions
Expand Down Expand Up @@ -238,12 +301,13 @@ func (link Link) onRelayComplete(msg *rpc.Message) {

func (link Link) registerWithSidecar() (bool, error) {
serviceInfo := rpc.ServiceInfo{
ServiceName: link.name,
ServiceVersion: link.version,
ServiceDescription: link.description,
ServiceType: rpc.ServiceInfo_ServiceType(rpc.ServiceInfo_ServiceType_value[link.serviceType]),
ServiceHost: link.cfg.ServerHost,
ServicePort: uint32(link.cfg.ServerPort),
ServiceName: link.name,
ServiceVersion: link.version,
ServiceDescription: link.description,
ServiceType: rpc.ServiceInfo_ServiceType(rpc.ServiceInfo_ServiceType_value[link.serviceType]),
ServiceHost: link.cfg.ServerHost,
ServicePort: uint32(link.cfg.ServerPort),
ServiceConfigTemplate: serviceConfigParamTemplates,
}

log.Info("Registering with sidecar",
Expand Down
69 changes: 69 additions & 0 deletions linker/linker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"strconv"
"testing"

"github.com/Axway/ace-golang-sdk/linker/rpcclient"
Expand Down Expand Up @@ -47,6 +48,74 @@ func TestRegister(t *testing.T) {
}
}

func TestAddParams(t *testing.T) {
AddStringConfigParam("string", "default", false)

if len(serviceConfigParamTemplates) != 1 {
t.Errorf("incorrect number of parameters, expected %s got %s", "1", strconv.Itoa(len(serviceConfigParamTemplates)))
}

if serviceConfigParamTemplates[0].GetType() != "string" {
t.Errorf("incorrect type of string parameter, expected %s got %s", "string", serviceConfigParamTemplates[0].GetType())
}

if serviceConfigParamTemplates[0].GetName() != "string" {
t.Errorf("incorrect name of string parameter, expected %s got %s", "string", serviceConfigParamTemplates[0].GetName())
}

if serviceConfigParamTemplates[0].GetDefaultValue() != "default" {
t.Errorf("incorrect default value of string parameter, expected %s got %s", "default", serviceConfigParamTemplates[0].GetDefaultValue())
}

if serviceConfigParamTemplates[0].GetIsRequired() != false {
t.Errorf("incorrect IsRequired of string parameter, expected %s got %s", "false", strconv.FormatBool(serviceConfigParamTemplates[0].GetIsRequired()))
}

AddIntConfigParam("integer", 123, true)

if len(serviceConfigParamTemplates) != 2 {
t.Errorf("incorrect number of parameters, expected %s got %s", "2", strconv.Itoa(len(serviceConfigParamTemplates)))
}

if serviceConfigParamTemplates[1].GetType() != "int" {
t.Errorf("incorrect type of integer parameter, expected %s got %s", "int", serviceConfigParamTemplates[1].GetType())
}

if serviceConfigParamTemplates[1].GetName() != "integer" {
t.Errorf("incorrect name of integer parameter, expected %s got %s", "integer", serviceConfigParamTemplates[1].GetName())
}

if serviceConfigParamTemplates[1].GetDefaultValue() != "123" {
t.Errorf("incorrect default value of integer parameter, expected %s got %s", "123", serviceConfigParamTemplates[1].GetDefaultValue())
}

if serviceConfigParamTemplates[1].GetIsRequired() != true {
t.Errorf("incorrect IsRequired of integer parameter, expected %s got %s", "true", strconv.FormatBool(serviceConfigParamTemplates[1].GetIsRequired()))
}

AddBooleanConfigParam("boolean", false)

if len(serviceConfigParamTemplates) != 3 {
t.Errorf("incorrect number of parameters, expected %s got %s", "3", strconv.Itoa(len(serviceConfigParamTemplates)))
}

if serviceConfigParamTemplates[2].GetType() != "boolean" {
t.Errorf("incorrect type of boolean parameter, expected %s got %s", "boolean", serviceConfigParamTemplates[2].GetType())
}

if serviceConfigParamTemplates[2].GetName() != "boolean" {
t.Errorf("incorrect name of boolean parameter, expected %s got %s", "boolean", serviceConfigParamTemplates[2].GetName())
}

if serviceConfigParamTemplates[2].GetDefaultValue() != "false" {
t.Errorf("incorrect default value of boolean parameter, expected %s got %s", "false", serviceConfigParamTemplates[2].GetDefaultValue())
}

if serviceConfigParamTemplates[2].GetIsRequired() != true {
t.Errorf("incorrect IsRequired of boolean parameter, expected %s got %s", "true", strconv.FormatBool(serviceConfigParamTemplates[2].GetIsRequired()))
}
}

type mockClient struct {
buildClientRelayCalled bool
clientRegisterCalled bool
Expand Down
Loading

0 comments on commit 916447d

Please sign in to comment.