All Projects → agiledragon → trans-dsl

agiledragon / trans-dsl

Licence: MIT license
a transaction model framework, seems simple but powerful

Programming Languages

go
31211 projects - #10 most used programming language

Projects that are alternatives of or similar to trans-dsl

oggm
Open Global Glacier Model
Stars: ✭ 167 (+160.94%)
Mutual labels:  model
OGMNeo
[No Maintenance] Neo4j nodeJS OGM(object-graph mapping) abstraction layer
Stars: ✭ 54 (-15.62%)
Mutual labels:  transaction
orchestrate-node
This Orchestrate library provides convenient access to the Orchestrate API from applications written in server-side NodeJS
Stars: ✭ 19 (-70.31%)
Mutual labels:  transaction
DTE
Generate C# class from database table
Stars: ✭ 26 (-59.37%)
Mutual labels:  model
virtio
Virtio implementation in SystemVerilog
Stars: ✭ 38 (-40.62%)
Mutual labels:  model
hcv-color
🌈 Color model HCV/HCG is an alternative to HSV and HSL, derived by Munsell color system, usable for Dark and Light themes... 🌈
Stars: ✭ 44 (-31.25%)
Mutual labels:  model
SpleeterRT
Real time monaural source separation base on fully convolutional neural network operates on Time-frequency domain.
Stars: ✭ 111 (+73.44%)
Mutual labels:  model
ModelSynchro
A JSON model generator for Swift 4
Stars: ✭ 19 (-70.31%)
Mutual labels:  model
Transactional-NTFS-TxF-.NET
Transactional NTFS (TxF) Library .NET is a small library .Net (C#) allows to use transactions on NTFS FileSystem (Transactional NTFS (TxF))
Stars: ✭ 20 (-68.75%)
Mutual labels:  transaction
realar
5 kB Advanced state manager for React
Stars: ✭ 41 (-35.94%)
Mutual labels:  model
acorn-db
Provides Acorn projects with Eloquent Models for WordPress data.
Stars: ✭ 30 (-53.12%)
Mutual labels:  model
eSelection
Dynamic model selection library for SA-MP servers
Stars: ✭ 28 (-56.25%)
Mutual labels:  model
SimpleCoin
A simple cryptocurrency application for educational purposes only.
Stars: ✭ 13 (-79.69%)
Mutual labels:  transaction
android-upi-payment
An android library for integrating payment using existing upi apps.
Stars: ✭ 26 (-59.37%)
Mutual labels:  transaction
darkgreybox
DarkGreyBox: An open-source data-driven python building thermal model inspired by Genetic Algorithms and Machine Learning
Stars: ✭ 25 (-60.94%)
Mutual labels:  model
MySQL-cheatsheet
Cheatsheet for MySQL
Stars: ✭ 43 (-32.81%)
Mutual labels:  transaction
mxfactorial
a payment application intended for deployment by the united states treasury
Stars: ✭ 36 (-43.75%)
Mutual labels:  transaction
Apriori-and-Eclat-Frequent-Itemset-Mining
Implementation of the Apriori and Eclat algorithms, two of the best-known basic algorithms for mining frequent item sets in a set of transactions, implementation in Python.
Stars: ✭ 36 (-43.75%)
Mutual labels:  transaction
KuiBaDB
Another OLAP database
Stars: ✭ 297 (+364.06%)
Mutual labels:  transaction
Odin
manage model revisions with ease
Stars: ✭ 60 (-6.25%)
Mutual labels:  model

Transaction DSL

trans-dsl is a transaction model framework in golang, can be used in complex business scenes.

Introduction

In some complex domains, a business process may involve the interaction of multiple messages, which may be synchronous messages, asynchronous messages, or even system calls. We explicitly model the operation behavior of message interaction, and call the operation behavior of a message interaction as Action, then the flow chart of the business corresponds to an Action sequence. In general, a single scenario business process corresponds to a transaction process. Based on the transaction model framework, business developers can not only express all business processes in a simple and complete manner at a higher level, but also apply and maintain transaction models at low cost.

Chinese reader references

Features

  • support synchronous messages
  • support system calls
  • support asynchronous messages

Installation


$ go get github.com/agiledragon/trans-dsl

Keyword

  • optional
  • loop
  • retry
  • repeat
  • wait
  • concurrent
  • procedure
  • succ
  • fail
  • not
  • allof
  • anyof

Using Transaction DSL

Firstly, let's look at a transaction example.

During the synchronization request processing from S1 to S2, the perspective of standing at S1 is an Action, while the perspective of standing at S2 is a transaction.

func newS1ReqTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			new(action.Action1),
			&transdsl.Optional{
				Spec:   new(spec.ShouldExecAction2),
				IfFrag: new(action.Action2),
			},
			&transdsl.Loop{
				FuncVar: newProcedure1,
			},
			new(action.Action3),
		},
	}
	return trans
}

func newProcedure1() transdsl.Fragment {
	procedure := &transdsl.Procedure{
		Fragments: []transdsl.Fragment{
			new(action.Action11),
			&transdsl.Optional{
				Spec:   new(spec.ShouldExecProcedure2),
				IfFrag: newProcedure2(),
			},
			new(action.Action12),
		},
	}
	return procedure
}

func newProcedure2() transdsl.Fragment {
	procedure := &transdsl.Procedure{
		Fragments: []transdsl.Fragment{
			new(action.Action21),
			new(action.Action22),
		},
	}
	return procedure
}

The following just make some tests as typical examples. Please refer to the test cases, very complete and detailed.

optional

import (
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	"github.com/agiledragon/trans-dsl/test/context/spec"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func newIfTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			&transdsl.Optional{
				Spec:   new(spec.IsAbcExist),
				IfFrag: new(action.StubConnectAbc),
			},
			new(action.StubActivateSomething),
		},
	}
	return trans
}

func newElseTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			&transdsl.Optional{
				Spec:     new(spec.IsAbcExist),
				IfFrag:   new(action.StubConnectAbc),
				ElseFrag: new(action.StubConnectDef),
			},
			new(action.StubActivateSomething),
		},
	}
	return trans
}

func TestIfTrans(t *testing.T) {
	trans := newIfTrans()
	Convey("TestIfTrans", t, func() {

		Convey("trans exec succ when spec is true", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					Abc: "abc",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 2)
		})

		Convey("trans exec succ when spec is false", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					Abc: "def",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 1)
		})

		Convey("iffrag rollback", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					X:   "test",
					Abc: "abc",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldNotEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 0)
		})
	})
}

func TestElseTrans(t *testing.T) {
	trans := newElseTrans()
	Convey("TestElseTrans", t, func() {

		Convey("trans exec succ when spec is false", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					Abc: "def",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 3)
		})

		Convey("elsefrag rollback", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					X:   "test",
					Abc: "def",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldNotEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, -1)
		})
	})
}

loop

import (
	"errors"
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func newLoopTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			new(action.StubGetSomething),
			&transdsl.Loop{
				FuncVar:      newLoopProcedure,
				BreakErrs:    []error{errors.New("break1"), errors.New("break2")},
				ContinueErrs: []error{errors.New("continue1"), errors.New("continue2")},
			},
		},
	}
	return trans
}

func newLoopProcedure() transdsl.Fragment {
	procedure := &transdsl.Procedure{
		Fragments: []transdsl.Fragment{
			new(action.StubAttachSomething),
			new(action.StubActivateSomething),
		},
	}
	return procedure
}

func TestLoopTrans(t *testing.T) {
	trans := newLoopTrans()
	Convey("TestLoopTrans", t, func() {

		Convey("trans exec succ when loop 3 times", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					LoopValue: 1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).LoopValue, ShouldEqual, 4)
		})

		Convey("trans exec succ when second time break", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					LoopValue: 1,
					Abc:       "break",
				},
			}
			err := trans.Start(transInfo)
			So(err.Error(), ShouldEqual, "break2")
			So(transInfo.AppInfo.(*context.StubInfo).LoopValue, ShouldEqual, 2)

		})

		Convey("trans exec succ when third time continue", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					LoopValue: 1,
					Abc:       "continue",
				},
			}
			err := trans.Start(transInfo)
			So(err.Error(), ShouldEqual, "continue2")
			So(transInfo.AppInfo.(*context.StubInfo).LoopValue, ShouldEqual, 3)

		})

		Convey("trans exec fail", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					LoopValue: 1,
					X:         "test",
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldNotEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).LoopValue, ShouldEqual, 1)
		})
	})
}

retry

import (
	"errors"
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func newRetryTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			&transdsl.Retry{
				MaxTimes: 3,
				TimeLen:  100,
				Fragment: new(action.StubConnectServer),
				Errs:     []error{errors.New("fatal"), errors.New("panic")},
			},
		},
	}
	return trans
}

func TestRetryTrans(t *testing.T) {
	trans := newRetryTrans()
	Convey("TestRetryTrans", t, func() {

		Convey("trans exec succ when fail time is 1", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					FailTimes: 1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
		})

		Convey("trans exec succ when fail time is 2", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					FailTimes: 2,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
		})

		Convey("trans exec fail when fail time is 3", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					FailTimes: 3,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldNotEqual, nil)
		})

		Convey("trans exec fail when error string is panic", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					FailTimes: 1,
					Y:         -1,
				},
			}
			err := trans.Start(transInfo)
			So(err.Error(), ShouldEqual, "panic")
		})
	})
}

wait

import (
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
	"time"
)

var eventId = "assign cmd"

func newWaitTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			new(action.StubTransferMoney),
			&transdsl.Wait{
				EventId:  eventId,
				Timeout:  100,
				Fragment: new(action.StubAssignCmd),
			},
			new(action.StubAttachSomething),
			new(action.StubActivateSomething),
		},
	}
	return trans
}

type TransObj struct {
	trans     *transdsl.Transaction
	transInfo *transdsl.TransInfo
}

var transIds map[string]string
var transObjs map[string]TransObj
var key string

func handleEvent(eventId string, eventContent []byte) {
	transId := transIds[key]
	transObj := transObjs[transId]
	trans := transObj.trans
	transInfo := transObj.transInfo
	stubInfo := transInfo.AppInfo.(*context.StubInfo)
	stubInfo.EventContent = eventContent
	<-time.After(50 * time.Millisecond)
	trans.HandleEvent(eventId, transInfo)
}

func TestWaitTrans(t *testing.T) {
	trans := newWaitTrans()
	transIds = make(map[string]string)
	key = "business id"
	transId := "123456"
	transIds[key] = transId

	transObjs = make(map[string]TransObj)
	transInfo := &transdsl.TransInfo{
		Ch: make(chan struct{}),
		AppInfo: &context.StubInfo{
			TransId: "",
			X:       "info",
			Y:       1,
		},
	}
	transObjs[transId] = TransObj{trans: trans, transInfo: transInfo}
	Convey("TestWaitTrans", t, func() {

		Convey("wait succ", func() {
			go handleEvent(eventId, nil)
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 8)
		})

		Convey("wait timeout", func() {
			transInfo := &transdsl.TransInfo{
				Ch: make(chan struct{}),
				AppInfo: &context.StubInfo{
					X: "info",
					Y: 1,
				},
			}
			err := trans.Start(transInfo)
			So(err.Error(), ShouldEqual, transdsl.ErrTimeout.Error())
		})
	})
}

allof

import (
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	"github.com/agiledragon/trans-dsl/test/context/spec"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func newAllOfTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			&transdsl.Optional{
				Spec: &transdsl.AllOf{
					Specs: []transdsl.Specification{
						new(spec.IsAbcExist),
						new(spec.IsDefExist),
						new(spec.IsGhiExist),
					},
				},
				IfFrag: new(action.StubConnectAbc),
			},
			new(action.StubActivateSomething),
		},
	}
	return trans
}

func TestAllOfTrans(t *testing.T) {
	trans := newAllOfTrans()
	Convey("TestAllOfTrans", t, func() {

		Convey("all specs are true", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					Abc: "abc",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 2)
		})

		Convey("one of specs is false", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					Abc: "def",
					Y:   1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).Y, ShouldEqual, 1)
		})
	})
}

fail

import (
	"errors"
	"github.com/agiledragon/trans-dsl"
	"github.com/agiledragon/trans-dsl/test/context"
	"github.com/agiledragon/trans-dsl/test/context/action"
	"github.com/agiledragon/trans-dsl/test/context/spec"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

var ErrResourceInsufficient = errors.New("resource insufficient")

func newFailTrans() *transdsl.Transaction {
	trans := &transdsl.Transaction{
		Fragments: []transdsl.Fragment{
			new(action.StubAttachSomething),
			&transdsl.Optional{
				Spec: new(spec.IsSomeResourceInsufficient),
				IfFrag: &transdsl.Fail{
					ErrCode: ErrResourceInsufficient,
				},
			},
			new(action.StubActivateSomething),
		},
	}
	return trans
}

func TestFailTrans(t *testing.T) {
	trans := newFailTrans()
	Convey("TestFailTrans", t, func() {

		Convey("spec is true", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					X:          "insufficient",
					SpecialNum: 1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, ErrResourceInsufficient)
			So(transInfo.AppInfo.(*context.StubInfo).SpecialNum, ShouldEqual, 10)
		})

		Convey("spec is false", func() {
			transInfo := &transdsl.TransInfo{
				AppInfo: &context.StubInfo{
					X:          "sufficient",
					SpecialNum: 1,
				},
			}
			err := trans.Start(transInfo)
			So(err, ShouldEqual, nil)
			So(transInfo.AppInfo.(*context.StubInfo).SpecialNum, ShouldEqual, 20)
		})
	})
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].