Documentation ¶
Overview ¶
Copyright (c) 2021 Alexandru Catrina
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Index ¶
- Constants
- func FromJson(src []byte, into interface{}) error
- func Install(db *gorm.DB) error
- func NewPullRequest(reg Registry, ctx PullContext) (out []byte, err error)
- func NewPushRequest(reg Registry, ctx PushContext) (out []byte, err error)
- func ToJson(src interface{}) ([]byte, error)
- func Uninstall(db *gorm.DB) error
- type Actor
- type Actors
- type Details
- type Label
- type Labels
- type NullString
- type PullContext
- type PushContext
- type Registry
- type Transaction
- type Transactions
Constants ¶
const ( ModVersion = "0.3.2" DateFormat = "Mon 02 Jan 2006" // Layout used instead of "d m Y" abbr )
Variables ¶
This section is empty.
Functions ¶
func FromJson ¶
FromJson is a tiny helper function to deserialize a JSON payload into one of the registry key components (Actors, Labels, Transactions)
func Install ¶
Install is a helper function to create and migrate the required tables on a supported database. Failure to install returns errors and must be handled by the caller
func NewPullRequest ¶
func NewPullRequest(reg Registry, ctx PullContext) (out []byte, err error)
NewPullRequest is promoted as the primary entrypoint to use/handle supported registry component (Actors, Labels, Transactions) because of it's wrapped on the repetitive pipeline to list existing records; obtain a copy of the items in the form of Golang structs and JSON as well. Upon failure an error yields instead and none or incomplete records are actually being *pulled*
func NewPushRequest ¶
func NewPushRequest(reg Registry, ctx PushContext) (out []byte, err error)
NewPushRequest is promoted as the primary entrypoint to use/handle supported registry component (Actors, Labels, Transactions) because of it's wrapped on the repetitive pipeline to create new records and obtain a copy of the items in the form of Golang structs and JSON as well. Upon failure an error yields instead and none or incomplete records are actually being *pushed*
Types ¶
type Actor ¶
type Actor struct { Name string `json:"name" gorm:"type: varchar(100); primaryKey"` Flags uint16 `json:"flags" gorm:"not null"` Headers string `json:"headers" gorm:"type: text; not null"` CreatedAt time.Time `json:"-" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"-" gorm:"autoUpdateTime"` }
Actor is one of the key components of the expenses module. An actor is an abstraction of any participant in a transaction. Currenly its use is to differenciate between *senders* and *receivers*
func NewActor ¶
NewActor is an idiomatic constructor for the Actor entity. This method doesn't handle meta fields such as Flags or Headers
func (*Actor) BeforeCreate ¶
BeforeCreate hook from GORM to check if actors has valid name
type Actors ¶
type Actors []Actor
Actors is a registry-type that represents a collection of its appropriate *Actor* entities
func (*Actors) Pull ¶
func (a *Actors) Pull(ctx PullContext) error
Pull enables to read actors from registry. The results are always sorted by their name
func (*Actors) Push ¶
func (a *Actors) Push(ctx PushContext) error
Push enables to write new actors into registry or updates the fields of the existing ones if a *name* conflict occurs
type Details ¶
type Details struct { UUID *string `json:"-" gorm:"type: varchar(36); primaryKey"` TransactionUUID string `json:"-" gorm:"not null"` LabelName string `json:"label" gorm:"not null"` Amount int64 `json:"amount" gorm:"not null"` Flags uint16 `json:"flags" gorm:"not null"` Headers string `json:"headers" gorm:"type: text; not null"` CreatedAt time.Time `json:"-" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"-" gorm:"autoUpdateTime"` Transaction *Transaction `json:"-" gorm:"foreignKey: TransactionUUID"` Label *Label `json:"-" gorm:"foreignKey: LabelName; constraint: OnUpdate:CASCADE"` }
Details is an adjacent component of the expenses module to support the *amount* breakdown of a Transaction into multiple records individually labeled.
This is the *only* entity that's created indirectly from a Transaction and cannot have its fields updated in any way
func (*Details) BeforeCreate ¶
BeforeCreate hook from GORM to generate an UUID just before creating the new entry for the transaction details
type Label ¶
type Label struct { Name string `json:"name" gorm:"type: varchar(100); primaryKey"` ParentName NullString `json:"parent" gorm:"type: varchar(100)"` Flags uint16 `json:"flags" gorm:"not null"` Headers string `json:"headers" gorm:"type: text; not null"` CreatedAt time.Time `json:"-" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"-" gorm:"autoUpdateTime"` Parent *Label `json:"-" gorm:"foreignKey: ParentName"` }
Label is another key component of the expenses module. A label is an user-defined entity used to classify transactions through meta information. The label has a tree-like structure where any entity can be the parent of any other entities, while the root entity is always present with NULL value for parent field
func NewLabel ¶
NewLabel is an idiomatic constructor for the Label entity. This method doesn't handle meta fields such as Flags or Headers
func (*Label) BeforeCreate ¶
BeforeCreate hook from GORM to correctly attach the parent name from the relationship, if any; and validate provided name
type Labels ¶
type Labels []Label
Labels is a registry-type that represents a collection of its appropriate *Label* entities
func (*Labels) Pull ¶
func (l *Labels) Pull(ctx PullContext) error
Pull enables to read labels from registry. The results are always sorted by their name and contain the parents of the already retrieved labels as well
func (*Labels) Push ¶
func (l *Labels) Push(ctx PushContext) error
Push enables to write new labels into registry or updates the fields of the existing ones if a *name* conflict occurs. Each label can have a parent label to link with
type NullString ¶
type NullString struct {
sql.NullString
}
NullString is a compatible SQL and JSON structure, mostly added to support the tree structure of Label's optional Parent
func (NullString) MarshalJSON ¶
func (ns NullString) MarshalJSON() ([]byte, error)
MarshalJSON to properly support NULL conversion (see above)
func (*NullString) UnmarshalJSON ¶
func (ns *NullString) UnmarshalJSON(b []byte) error
UnmarshalJSON to properly support NULL conversion (the problem was actually sql.NullString not having this conversion)
type PullContext ¶
type PullContext struct { // Storage is mainly Maria/MySQL with little support for SQLite Storage *gorm.DB // Limit is the equivalent of SQL LIMIT statement. By default it // isn't set on query because of the default zero value Limit int // Offset is the equivalent of SQL OFFSET statement on query. It // is similar to Limit, by default it isn't set Offset int }
PullContext is complement with PushContext (see above)
The use of PULL is to retrieve records
type PushContext ¶
type PushContext struct { // Storage is mainly Maria/MySQL with little support for SQLite Storage *gorm.DB // BatchSize must be a positive number above zero, otherwise it // will fail to write BatchSize int // JustAppend is mostly used internally to upsert only and don't // propage updates to all fields JustAppend bool }
PushContext is a thin wrapper to "explain" to an entity what needs to be pushed into registry since the registry it's not aware of the underlying persistence layer
The use of PUSH is to create records through upsert and update fields on conflict
type Registry ¶
type Registry interface { Push(PushContext) error Pull(PullContext) error }
Registry is defined as an unified simplistic API developed to interact with an underlaying persistence layer via only two routes. The concept of the expenses module is visualy defined by its component parts below
(Participants) Actor Label (User defined label to identify scope) | | \ /| Transaction | \_ / Details (Transaction breakdown for the amount)
A *Transaction* is a registry entry that demonstrates an exchange between two participants (Actors) for a given amount (base currency) with one or many declared scopes (Transaction Label and Details Labels)
Only *collections* of records are considered valid candidates to implement this interface since the concept of the registry works with sets of items
type Transaction ¶
type Transaction struct { UUID *string `json:"uuid,omitempty" gorm:"type: varchar(36); primaryKey"` Date time.Time `json:"date" gorm:"type: date; index; not null"` Amount int64 `json:"amount" gorm:"not null"` LabelName string `json:"label" gorm:"not null"` SenderName string `json:"sender" gorm:"not null"` ReceiverName string `json:"receiver" gorm:"not null"` Signature string `json:"signature" gorm:"type: varchar(36); index; not null"` Flags uint16 `json:"flags" gorm:"not null"` Headers string `json:"headers" gorm:"type: text; not null"` CreatedAt time.Time `json:"-" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"-" gorm:"autoUpdateTime"` Label *Label `json:"-" gorm:"foreignKey: LabelName; constraint: OnUpdate:CASCADE"` Sender *Actor `json:"-" gorm:"foreignKey: SenderName; constraint: OnUpdate:CASCADE"` Receiver *Actor `json:"-" gorm:"foreignKey: ReceiverName; constraint: OnUpdate:CASCADE"` Details []*Details `json:"details" gorm:"foreignKey: TransactionUUID; references: UUID"` }
Transaction *is* the key component of the expenses module which bounds together foreign Actors and Labels. Any transaction entity is actually the equivalent of a real-world transaction between two parties, namely a Sender and a Receiver, which are both Actors. Meta information about the transaction itself is made through user-defined Labels
A transaction cannot change its *date* and payment *amount* afterwards but it allows to update its Actors and Labels, as long as they exists; and most importantly: the *amount* field is used to interpret the type of the transaction as a binary operation (IN > 0 otherwise OUT)
func NewTransaction ¶
func NewTransaction(d time.Time, a int64, lb Label, tx, rx Actor, ls map[Label]int64, z string) Transaction
NewTransaction is the primary idiomatic constructor for the Transaction entity from which all other records can derive. Transaction details can be omitted by providing nil map. This method doesn't handle meta fields such as Flags or Headers
func (*Transaction) BeforeCreate ¶
func (t *Transaction) BeforeCreate(tx *gorm.DB) (err error)
BeforeCreate hook from GORM to generate an UUID just before creating the new entry for the transaction. The use of UUID as string instead of binary is due to JSON (un)marshal and portability over ASCII only communication channels
This method is responsible for constraints check upon amount details preventing the introduction of incomplete or corrupted transactions
func (*Transaction) String ¶
func (t *Transaction) String() string
String representation of a transaction with all its fields and ahead of time resolution of the relationship between components. An output may contain transaction details as well
type Transactions ¶
type Transactions []Transaction
Transactions is a registry-type that represents a collection of its appropriate *Transaction* entities. This is the primary registry of the expenses module
func (*Transactions) Pull ¶
func (t *Transactions) Pull(ctx PullContext) error
Pull from registry automatically resolves the relationship between these three components (Actors, Labels, Details) and the results are sorted by the descending date & amount of the real-world transaction authorization
func (*Transactions) Push ¶
func (t *Transactions) Push(ctx PushContext) error
Push into registry *must* always succeed to write a list of transactions in the persistent layer, whether it requires additional Actors/Labels to be written before the actual commit or to add details after the commit