Engine: Scripting support (#383)

* WIP

* updated appveyor and increased deadline 5 seconds due to increased linters being added

* revert files to upstream/engine

* WIP

* WIP

* mod file changes

* added script manager

* Added manager/and cli interfaces to scripting

* Added script task handler

* WIP - Added timer/repeat support and fleshed out wrapper further

* autoload support added + WIP

* WIP commit

* added account balance info

* btc markets temp work around

* WIP - merged with upstream for new order package BTC Markets responses broken

* Cancel order wrapper WIP

*  order wrapper update

* Added test coverage for VM

* moved to map for VM List shutdown of all VM now handled added gctcli commands for list and stop of running scripts

* added override to load/execute for path

* fixed incorrect channel shutdown added further test coverage and restructured gctcli commands into sub commands

* increased test coverage for packages

* Added docs cleaned up tests and example scripts

* Test coverage increased for module/gct/exchange package

* windows fixes

* merged upstream/engine

* WIP

* logger fixes - removed pointer to bool check removed duplicate test check for logger

* remove unused mutex

* added inital upload support

* fix linter issues for go-fmt

* added zip support for uploading and added base for fund withdrawing

* changed error return types and also log errors, fix zip path issue

* improved error outputs and code flow

* pairs response fix added protobuf defs for stop all and list all

* added stop all running scripts general clean up and moved across to OrderManager

* linter fixes (gofmt)

* added list all command

* rewrote zip handler to be cleaner also fixed file overwrite on upload

* added query command reworked tests

* added further error checking to compileandrun corrected use of pointers for accountinfo

* bumped tengo version

* Removed named returns reworded log messages removed unused falseptr

* WIP

* Added virutal machine limit improved config options

* added model for script event added upload validation

* script_event table has been completed, tests for wrapper functions implemented

* README updates

* reverted changes opened new PR to move withdraw struct outs

* intial work on adding withdraw support after merger of withdraw package

* started work on examples

* Added crypto withdraw support

* fix switch case assignment and gofmt project

* Reworking Fiat withdraw request pending #402

* removed double pointer call

* added withdraw support for fiat  currencies

* added tests for withdraw methods increased readme

* removed local tengo require and also fix linter issues

* Added default log size const added basic test for invalid script execution

* First pass at moving wrapper to validator package to allow proper validation of uploaded scripts

* Added script details to README added config test added test for no file extension

* moved tests to const and fixed incorrect pathing

* added test coverage to withdraw package

* corrected file close handling

* point to included configtest.json

* extended validator support when a script is uploaded

* Bug fix on bool logic

* Added mutex

* Don't create autit events on test execution

* reverted common to master

* moved file rename to unix timestamp format

* converted logger enabled back to pointer as i need nilness check also moved scriptid to text over blob

* started work on autoload add/remove support

* First round of PR fixes (mostly commented exports)

* Moved GCTScript load to last, removed unneeded error from cleanup()

* Comment clairty for AuitEventID

* added autoload add/remove command to cli

* added tests for autoload

* Test updates for Exchanges

* linter fixes (gofmt)

* Removed double check of engine pointer

* remove possible nil pointer on GetSpecificTicker

* Fixed not closing file handler on write that causes archive removal to fail

* file handler Close clean ups

* corrected spelling on error return and return invalid name n autoload

* moved strings to cosnt moved bool pointer creation to convert package

* new zip extractor added

* Validation has been added to archive uploads

* removed shadow var on err

* added ok check to conversion

* converted condition check

* basic test for zip extract added

* new zip handler

* reverted back to old atomic loading system

* removed shadow err

* lets add a new line

* added space to error return

* command line toggle for script now works properly

* readme updated

* set configLoaded to true

* check for configLoaded condition

* added mutex to allow for multiple access on virtual machine increased test coverage disable script manager if scripting is disabled

* linked up to enable/disablesubsystem commands

* added start/stop example to readme

* reworked logic on test as check should be done on Load()

* updated to tengo v2

* linters

* lower time on ntp client to stop slippage

* remove all fails if any fail validtion from an archive

* remove vm from list if timer is invalid

* removed shadow on err

* remove config creation from NTPCheck test

* WIP testing DB changes

* add unique constraint

* WIP: created has many model

* linters run

* basic sqlite3 support added for new database format

* linters run

* Added test coverage for script repo

* removed unused print

* updated env vars for CI instances

* updated env vars for CI instances

* Updated test packages

* Test updates for postgresql

* removed invalid tests from postgres

* remove duplication of struct and improved code flow

* general cleanup

* wording changes on log output

* use databasemgr logger and add support for autoload without file extension

* corrected test naming

* return correct error

* return correct error again version 82

* store scriptdata on creation

* Hello

* Errorln -> Errorf

* Removed unused vars

* Read me updates

* testing without parallel

* comment on exported type

* added nil check against VM for test

* add debugging information

* gofmt

* remove verbose and data sent to channel

* Added debug information

* linter fixes (gofmt)

* remove unused CompileAndRun() call

* test sleep to see if issue is timing related

* semi-concurrent map fixes

* one day i will run gofmt or setup precommit hooks

* new line :D

* increased test coverage

* added correct sleep time

* Moved over to sync map

* linter fixes (gofmt)

* goimports

* moved VM related methods to vm.go

* new line at end of file

* trying increased timeout on golangci-lint for appveyor

* add debugging information

* removed timeout

* reworked timeout logic

* linter fixes (gofmt)

* increased test coverage

* increased test coverage

* one day i will run gofmt or setup precommit hooks

* removed unused exchange test

* increased golangci-lint timeout

* Added nil check on shutdown and test coverage for it lowered timeout back to 1:30

* reworked ID system

* removed script hash as it was unused

* added comments on exported methods and read me update

* reorder code

* removed to atomic.value for test execution flag

* increased test coverage

* move add further up execution

* point to correct script file
This commit is contained in:
Andrew
2020-01-23 13:54:04 +11:00
committed by Adrian Gallagher
parent 4c33a0738a
commit f6fd94ea69
102 changed files with 15181 additions and 370 deletions

View File

@@ -24,7 +24,8 @@ environment:
PSQL_PASS: Password12!
PSQL_DBNAME: gct_dev_ci
PSQL_SSLMODE: disable
PSQL_SKIPSQLCMD: true
PSQL_TESTDBNAME: gct_dev_ci
stack: go 1.13.x
services:

View File

@@ -1,5 +1,5 @@
run:
timeout: 1m0s
timeout: 1m30s
issues-exit-code: 1
tests: true
skip-dirs:

View File

@@ -24,6 +24,8 @@ matrix:
- PSQL_USER=postgres
- PSQL_HOST=localhost
- PSQL_DBNAME=gct_dev_ci
- PSQL_SKIPSQLCMD=true
- PSQL_TESTDBNAME=gct_dev_ci
install: true
cache:
directories:
@@ -48,6 +50,8 @@ matrix:
- PSQL_USER=postgres
- PSQL_HOST=localhost
- PSQL_DBNAME=gct_dev_ci
- PSQL_SKIPSQLCMD=true
- PSQL_TESTDBNAME=gct_dev_ci
install: true
cache:
directories:

View File

@@ -72,6 +72,7 @@ We are aiming to support the top 20 highest volume exchanges based off the [Coin
+ Packages for handling currency pairs, tickers and orderbooks.
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
+ Basic event trigger system.
+ Scripting support. See [gctscript](/gctscript/README.md)
+ WebGUI (discontinued).
## Planned Features

View File

@@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -2915,3 +2917,436 @@ func getAuditEvent(c *cli.Context) error {
jsonOutput(result)
return nil
}
var uuid, filename, path string
var gctScriptCommand = cli.Command{
Name: "gctscript",
Usage: "execute gctscript command",
ArgsUsage: "<command> <args>",
Subcommands: []cli.Command{
{
Name: "execute",
Usage: "execute script filename",
ArgsUsage: "<filename> <path>",
Flags: []cli.Flag{
cli.StringFlag{
Name: "filename",
Usage: "<filename>",
Destination: &filename,
},
cli.StringFlag{
Name: "path",
Usage: "<script path>",
Destination: &path,
},
},
Action: gctScriptExecute,
},
{
Name: "query",
Usage: "query running virtual machine",
Flags: []cli.Flag{
cli.StringFlag{
Name: "uuid",
Usage: "<uuid>",
Destination: &uuid,
},
},
Action: gctScriptQuery,
},
{
Name: "read",
Usage: "read script",
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "<name>",
Destination: &uuid,
},
},
Action: gctScriptRead,
},
{
Name: "status",
Usage: "get status of running scripts",
Action: gctScriptStatus,
},
{
Name: "list",
Usage: "lists all scripts in default scriptpath",
Action: gctScriptList,
},
{
Name: "stop",
Usage: "terminate running script",
Flags: []cli.Flag{
cli.StringFlag{
Name: "uuid",
Usage: "<uuid>",
Destination: &uuid,
},
},
Action: gctScriptStop,
},
{
Name: "stopall",
Usage: "terminate running script",
Action: gctScriptStopAll,
},
{
Name: "upload",
Usage: "upload a new script/archive",
Flags: []cli.Flag{
cli.StringFlag{
Name: "path",
Usage: "<path> to single script or zip collection",
Destination: &filename,
},
cli.BoolFlag{
Name: "overwrite",
Usage: "<true/false>",
},
cli.BoolFlag{
Name: "archived",
Usage: "<true/false>",
},
},
Action: gctScriptUpload,
},
{
Name: "autoload",
Usage: "add or remove script from autoload list",
Flags: []cli.Flag{
cli.StringFlag{
Name: "command",
Usage: "<add/remove>",
},
cli.StringFlag{
Name: "script",
Usage: "<script name>",
},
},
Action: gctScriptAutoload,
},
},
}
func gctScriptAutoload(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
var command, script string
var status bool
if !c.IsSet("command") {
if c.Args().Get(0) != "" {
command = c.Args().Get(0)
}
}
if !c.IsSet("script") {
if c.Args().Get(1) != "" {
script = c.Args().Get(1)
}
}
switch command {
case "add":
status = false
case "remove":
status = true
default:
_ = cli.ShowSubcommandHelp(c)
return nil
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptAutoLoadToggle(context.Background(),
&gctrpc.GCTScriptAutoLoadRequest{
Script: script,
Status: status,
})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptExecute(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
if !c.IsSet("filename") {
if c.Args().Get(0) != "" {
filename = c.Args().Get(0)
}
}
if !c.IsSet("path") {
if c.Args().Get(1) != "" {
path = c.Args().Get(1)
}
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptExecute(context.Background(),
&gctrpc.GCTScriptExecuteRequest{
Script: &gctrpc.GCTScript{
Name: filename,
Path: path,
},
})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptStatus(c *cli.Context) error {
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptStatus(context.Background(),
&gctrpc.GCTScriptStatusRequest{})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptList(c *cli.Context) error {
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptListAll(context.Background(),
&gctrpc.GCTScriptListAllRequest{})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptStop(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
if !c.IsSet("uuid") {
if c.Args().Get(0) != "" {
uuid = c.Args().Get(0)
}
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptStop(context.Background(),
&gctrpc.GCTScriptStopRequest{
Script: &gctrpc.GCTScript{UUID: uuid},
})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptStopAll(c *cli.Context) error {
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptStopAll(context.Background(),
&gctrpc.GCTScriptStopAllRequest{})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptRead(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
if !c.IsSet("name") {
if c.Args().Get(0) != "" {
uuid = c.Args().Get(0)
}
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptReadScript(context.Background(),
&gctrpc.GCTScriptReadScriptRequest{
Script: &gctrpc.GCTScript{
Name: uuid,
},
})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptQuery(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
if !c.IsSet("uuid") {
if c.Args().Get(0) != "" {
uuid = c.Args().Get(0)
}
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
executeCommand, err := client.GCTScriptQuery(context.Background(),
&gctrpc.GCTScriptQueryRequest{
Script: &gctrpc.GCTScript{
UUID: uuid,
},
})
if err != nil {
return err
}
jsonOutput(executeCommand)
return nil
}
func gctScriptUpload(c *cli.Context) error {
if c.NArg() == 0 && c.NumFlags() == 0 {
_ = cli.ShowSubcommandHelp(c)
return nil
}
var overwrite bool
var archived bool
if !c.IsSet("path") {
if c.Args().Get(0) != "" {
filename = c.Args().Get(0)
}
}
if c.IsSet("overwrite") {
overwrite = c.Bool("overwrite")
} else {
ow, err := strconv.ParseBool(c.Args().Get(1))
if err == nil {
overwrite = ow
}
}
if c.IsSet("archived") {
archived = c.Bool("archived")
} else {
ow, err := strconv.ParseBool(c.Args().Get(1))
if err == nil {
archived = ow
}
}
if filepath.Ext(filename) != ".gct" && filepath.Ext(filename) != ".zip" {
return errors.New("file type must be gct or zip")
}
file, err := os.Open(filename)
if err != nil {
return err
}
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
data, err := ioutil.ReadAll(file)
if err != nil {
return err
}
uploadCommand, err := client.GCTScriptUpload(context.Background(),
&gctrpc.GCTScriptUploadRequest{
ScriptName: filepath.Base(file.Name()),
Data: data,
Archived: archived,
Overwrite: overwrite,
})
if err != nil {
return err
}
jsonOutput(uploadCommand)
return nil
}

View File

@@ -133,6 +133,7 @@ func main() {
getTickerStreamCommand,
getExchangeTickerStreamCommand,
getAuditEventCommand,
gctScriptCommand,
}
err := app.Run(os.Args)

View File

@@ -107,3 +107,9 @@ func SplitFloatDecimals(input float64) (baseNum, decimalNum int64, err error) {
}
return baseNum, decimalNum, nil
}
// BoolPtr takes in boolen condition and returns pointer version of it
func BoolPtr(condition bool) *bool {
b := condition
return &b
}

View File

@@ -205,3 +205,14 @@ func TestSplitFloatDecimals(t *testing.T) {
t.Error("Conversion error")
}
}
func TestBoolPtr(t *testing.T) {
y := BoolPtr(true)
if !*y {
t.Fatal("true expected received false")
}
z := BoolPtr(false)
if *z {
t.Fatal("false expected received true")
}
}

View File

@@ -24,6 +24,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/database"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
@@ -1187,10 +1188,8 @@ func (c *Config) CheckLoggerConfig() error {
c.Logging = log.GenDefaultSettings()
}
f := func(f bool) *bool { return &f }(false)
if c.Logging.AdvancedSettings.ShowLogSystemName == nil {
c.Logging.AdvancedSettings.ShowLogSystemName = f
c.Logging.AdvancedSettings.ShowLogSystemName = convert.BoolPtr(false)
}
if c.Logging.LoggerFileConfig != nil {
@@ -1198,10 +1197,11 @@ func (c *Config) CheckLoggerConfig() error {
c.Logging.LoggerFileConfig.FileName = "log.txt"
}
if c.Logging.LoggerFileConfig.Rotate == nil {
c.Logging.LoggerFileConfig.Rotate = f
c.Logging.LoggerFileConfig.Rotate = convert.BoolPtr(false)
}
if c.Logging.LoggerFileConfig.MaxSize < 0 {
c.Logging.LoggerFileConfig.MaxSize = 100
if c.Logging.LoggerFileConfig.MaxSize <= 0 {
log.Warnf(log.Global, "Logger rotation size invalid, defaulting to %v", log.DefaultMaxFileSize)
c.Logging.LoggerFileConfig.MaxSize = log.DefaultMaxFileSize
}
log.FileLoggingConfiguredCorrectly = true
}
@@ -1218,6 +1218,30 @@ func (c *Config) CheckLoggerConfig() error {
return nil
}
func (c *Config) checkGCTScriptConfig() error {
m.Lock()
defer m.Unlock()
if c.GCTScript.ScriptTimeout <= 0 {
c.GCTScript.ScriptTimeout = gctscript.DefaultTimeoutValue
}
if c.GCTScript.MaxVirtualMachines == 0 {
c.GCTScript.MaxVirtualMachines = gctscript.DefaultMaxVirtualMachines
}
scriptPath := filepath.Join(common.GetDefaultDataDir(runtime.GOOS), "scripts")
err := common.CreateDir(scriptPath)
if err != nil {
return err
}
gctscript.ScriptPath = scriptPath
gctscript.GCTScriptConfig = &c.GCTScript
return nil
}
func (c *Config) checkDatabaseConfig() error {
m.Lock()
defer m.Unlock()
@@ -1623,6 +1647,11 @@ func (c *Config) CheckConfig() error {
return fmt.Errorf(ErrCheckingConfigValues, err)
}
err = c.checkGCTScriptConfig()
if err != nil {
log.Errorf(log.Global, "Failed to configure gctscript, feature has been disabled: %s\n", err)
}
c.CheckConnectionMonitorConfig()
c.CheckCommunicationsConfig()
c.CheckClientBankAccounts()

View File

@@ -9,6 +9,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/database"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/ntpclient"
)
@@ -1760,25 +1761,12 @@ func TestCheckLoggerConfig(t *testing.T) {
*c.Logging.AdvancedSettings.ShowLogSystemName {
t.Error("unexpected result")
}
err = c.LoadConfig(TestFile, true)
if err != nil {
t.Errorf("Failed to load config: %v", err)
}
err = c.CheckLoggerConfig()
if err != nil {
t.Errorf("Failed to create logger with user settings: reason: %v", err)
}
}
func TestDisableNTPCheck(t *testing.T) {
t.Parallel()
c := GetConfig()
err := c.LoadConfig(TestFile, true)
if err != nil {
t.Fatal(err)
}
var c Config
warn, err := c.DisableNTPCheck(strings.NewReader("w\n"))
if err != nil {
@@ -1804,6 +1792,23 @@ func TestDisableNTPCheck(t *testing.T) {
}
}
func TestCheckGCTScriptConfig(t *testing.T) {
t.Parallel()
var c Config
if err := c.checkGCTScriptConfig(); err != nil {
t.Error(err)
}
if c.GCTScript.ScriptTimeout != gctscript.DefaultTimeoutValue {
t.Fatal("unexpected value return")
}
if c.GCTScript.MaxVirtualMachines != gctscript.DefaultMaxVirtualMachines {
t.Fatal("unexpected value return")
}
}
func TestCheckDatabaseConfig(t *testing.T) {
t.Parallel()

View File

@@ -10,6 +10,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
"github.com/thrasher-corp/gocryptotrader/database"
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/portfolio"
)
@@ -87,6 +88,7 @@ type Config struct {
ConnectionMonitor ConnectionMonitorConfig `json:"connectionMonitor"`
Profiler ProfilerConfig `json:"profiler"`
NTPClient NTPClientConfig `json:"ntpclient"`
GCTScript gctscript.Config `json:"gctscript"`
Currency CurrencyConfig `json:"currencyConfig"`
Communications CommunicationsConfig `json:"communications"`
RemoteControl RemoteControlConfig `json:"remoteControl"`

View File

@@ -61,6 +61,14 @@
"allowedDifference": 50000000,
"allowedNegativeDifference": 50000000
},
"gctscript": {
"enabled": true,
"timeout": 60000000000,
"max_virtual_machines": 10,
"allow_imports": true,
"auto_load": [],
"verbose": false
},
"currencyConfig": {
"forexProviders": [
{

View File

@@ -0,0 +1,6 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP EXTENSION IF EXISTS pgcrypto;

View File

@@ -0,0 +1,8 @@
-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd

View File

@@ -0,0 +1,16 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE TABLE IF NOT EXISTS script
(
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
script_id text not null,
script_name varchar not null,
script_path varchar not null,
script_data bytea null,
last_executed_at TIMESTAMP DEFAULT (now() at time zone 'utc'),
created_at TIMESTAMP DEFAULT (now() at time zone 'utc'),
CONSTRAINT script_event_uniq UNIQUE (script_id)
);
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP TABLE script;

View File

@@ -0,0 +1,14 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE TABLE "script" (
id text not null primary key,
script_id text not null,
script_name text not null,
script_path text not NULL,
script_data blob null,
last_executed_at timestamp not null default CURRENT_TIMESTAMP,
created_at timestamp not null default CURRENT_TIMESTAMP
);
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP TABLE "script";

View File

@@ -0,0 +1,13 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE TABLE IF NOT EXISTS script_execution
(
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
script_id uuid REFERENCES script(id) ON DELETE CASCADE,
execution_type varchar NOT NULL,
execution_status varchar NOT NULL,
execution_time TIMESTAMP NOT NULL DEFAULT (now() at time zone 'utc')
);
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP TABLE script_execution;

View File

@@ -0,0 +1,14 @@
-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE TABLE IF NOT EXISTS "script_execution"
(
id integer not null primary key,
script_id text not null,
execution_type text NOT NULL,
execution_status text NOT NULL,
execution_time timestamp not null default CURRENT_TIMESTAMP,
FOREIGN KEY(script_id) REFERENCES script(id)
);
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP TABLE script_execution;

View File

@@ -568,7 +568,7 @@ func testAuditEventsSelect(t *testing.T) {
}
var (
auditEventDBTypes = map[string]string{`ID`: `bigint`, `Type`: `character varying`, `Identifier`: `character varying`, `Message`: `text`, `CreatedAt`: `timestamp with time zone`}
auditEventDBTypes = map[string]string{`ID`: `bigint`, `Type`: `character varying`, `Identifier`: `character varying`, `Message`: `text`, `CreatedAt`: `timestamp without time zone`}
_ = bytes.MinRead
)

View File

@@ -13,51 +13,64 @@ import "testing"
// Separating the tests thusly grants avoidance of Postgres deadlocks.
func TestParent(t *testing.T) {
t.Run("AuditEvents", testAuditEvents)
t.Run("Scripts", testScripts)
}
func TestDelete(t *testing.T) {
t.Run("AuditEvents", testAuditEventsDelete)
t.Run("Scripts", testScriptsDelete)
}
func TestQueryDeleteAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsQueryDeleteAll)
t.Run("Scripts", testScriptsQueryDeleteAll)
}
func TestSliceDeleteAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSliceDeleteAll)
t.Run("Scripts", testScriptsSliceDeleteAll)
}
func TestExists(t *testing.T) {
t.Run("AuditEvents", testAuditEventsExists)
t.Run("Scripts", testScriptsExists)
}
func TestFind(t *testing.T) {
t.Run("AuditEvents", testAuditEventsFind)
t.Run("Scripts", testScriptsFind)
}
func TestBind(t *testing.T) {
t.Run("AuditEvents", testAuditEventsBind)
t.Run("Scripts", testScriptsBind)
}
func TestOne(t *testing.T) {
t.Run("AuditEvents", testAuditEventsOne)
t.Run("Scripts", testScriptsOne)
}
func TestAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsAll)
t.Run("Scripts", testScriptsAll)
}
func TestCount(t *testing.T) {
t.Run("AuditEvents", testAuditEventsCount)
t.Run("Scripts", testScriptsCount)
}
func TestHooks(t *testing.T) {
t.Run("AuditEvents", testAuditEventsHooks)
t.Run("Scripts", testScriptsHooks)
}
func TestInsert(t *testing.T) {
t.Run("AuditEvents", testAuditEventsInsert)
t.Run("AuditEvents", testAuditEventsInsertWhitelist)
t.Run("Scripts", testScriptsInsert)
t.Run("Scripts", testScriptsInsertWhitelist)
}
// TestToOne tests cannot be run in parallel
@@ -102,20 +115,25 @@ func TestToManyRemove(t *testing.T) {}
func TestReload(t *testing.T) {
t.Run("AuditEvents", testAuditEventsReload)
t.Run("Scripts", testScriptsReload)
}
func TestReloadAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsReloadAll)
t.Run("Scripts", testScriptsReloadAll)
}
func TestSelect(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSelect)
t.Run("Scripts", testScriptsSelect)
}
func TestUpdate(t *testing.T) {
t.Run("AuditEvents", testAuditEventsUpdate)
t.Run("Scripts", testScriptsUpdate)
}
func TestSliceUpdateAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSliceUpdateAll)
t.Run("Scripts", testScriptsSliceUpdateAll)
}

View File

@@ -4,7 +4,11 @@
package postgres
var TableNames = struct {
AuditEvent string
AuditEvent string
Script string
ScriptExecution string
}{
AuditEvent: "audit_event",
AuditEvent: "audit_event",
Script: "script",
ScriptExecution: "script_execution",
}

View File

@@ -7,4 +7,5 @@ import "testing"
func TestUpsert(t *testing.T) {
t.Run("AuditEvents", testAuditEventsUpsert)
t.Run("Scripts", testScriptsUpsert)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,892 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package postgres
import (
"bytes"
"context"
"reflect"
"testing"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/randomize"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
var (
// Relationships sometimes use the reflection helper queries.Equal/queries.Assign
// so force a package dependency in case they don't.
_ = queries.Equal
)
func testScriptExecutions(t *testing.T) {
t.Parallel()
query := ScriptExecutions()
if query.Query == nil {
t.Error("expected a query, got nothing")
}
}
func testScriptExecutionsDelete(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := o.Delete(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsQueryDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := ScriptExecutions().DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsSliceDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptExecutionSlice{o}
if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsExists(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
e, err := ScriptExecutionExists(ctx, tx, o.ID)
if err != nil {
t.Errorf("Unable to check if ScriptExecution exists: %s", err)
}
if !e {
t.Errorf("Expected ScriptExecutionExists to return true, but got false.")
}
}
func testScriptExecutionsFind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
scriptExecutionFound, err := FindScriptExecution(ctx, tx, o.ID)
if err != nil {
t.Error(err)
}
if scriptExecutionFound == nil {
t.Error("want a record, got nil")
}
}
func testScriptExecutionsBind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = ScriptExecutions().Bind(ctx, tx, o); err != nil {
t.Error(err)
}
}
func testScriptExecutionsOne(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if x, err := ScriptExecutions().One(ctx, tx); err != nil {
t.Error(err)
} else if x == nil {
t.Error("expected to get a non nil record")
}
}
func testScriptExecutionsAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
scriptExecutionOne := &ScriptExecution{}
scriptExecutionTwo := &ScriptExecution{}
if err = randomize.Struct(seed, scriptExecutionOne, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err = randomize.Struct(seed, scriptExecutionTwo, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptExecutionOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptExecutionTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := ScriptExecutions().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 2 {
t.Error("want 2 records, got:", len(slice))
}
}
func testScriptExecutionsCount(t *testing.T) {
t.Parallel()
var err error
seed := randomize.NewSeed()
scriptExecutionOne := &ScriptExecution{}
scriptExecutionTwo := &ScriptExecution{}
if err = randomize.Struct(seed, scriptExecutionOne, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err = randomize.Struct(seed, scriptExecutionTwo, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptExecutionOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptExecutionTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 2 {
t.Error("want 2 records, got:", count)
}
}
func scriptExecutionBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func testScriptExecutionsHooks(t *testing.T) {
t.Parallel()
var err error
ctx := context.Background()
empty := &ScriptExecution{}
o := &ScriptExecution{}
seed := randomize.NewSeed()
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, false); err != nil {
t.Errorf("Unable to randomize ScriptExecution object: %s", err)
}
AddScriptExecutionHook(boil.BeforeInsertHook, scriptExecutionBeforeInsertHook)
if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeInsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterInsertHook, scriptExecutionAfterInsertHook)
if err = o.doAfterInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterInsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterSelectHook, scriptExecutionAfterSelectHook)
if err = o.doAfterSelectHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterSelectHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeUpdateHook, scriptExecutionBeforeUpdateHook)
if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeUpdateHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterUpdateHook, scriptExecutionAfterUpdateHook)
if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterUpdateHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeDeleteHook, scriptExecutionBeforeDeleteHook)
if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeDeleteHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterDeleteHook, scriptExecutionAfterDeleteHook)
if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterDeleteHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeUpsertHook, scriptExecutionBeforeUpsertHook)
if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeUpsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterUpsertHook, scriptExecutionAfterUpsertHook)
if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterUpsertHooks = []ScriptExecutionHook{}
}
func testScriptExecutionsInsert(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptExecutionsInsertWhitelist(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Whitelist(scriptExecutionColumnsWithoutDefault...)); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptExecutionToOneScriptUsingScript(t *testing.T) {
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var local ScriptExecution
var foreign Script
seed := randomize.NewSeed()
if err := randomize.Struct(seed, &local, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err := randomize.Struct(seed, &foreign, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
queries.Assign(&local.ScriptID, foreign.ID)
if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := local.Script().One(ctx, tx)
if err != nil {
t.Fatal(err)
}
if !queries.Equal(check.ID, foreign.ID) {
t.Errorf("want: %v, got %v", foreign.ID, check.ID)
}
slice := ScriptExecutionSlice{&local}
if err = local.L.LoadScript(ctx, tx, false, (*[]*ScriptExecution)(&slice), nil); err != nil {
t.Fatal(err)
}
if local.R.Script == nil {
t.Error("struct should have been eager loaded")
}
local.R.Script = nil
if err = local.L.LoadScript(ctx, tx, true, &local, nil); err != nil {
t.Fatal(err)
}
if local.R.Script == nil {
t.Error("struct should have been eager loaded")
}
}
func testScriptExecutionToOneSetOpScriptUsingScript(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a ScriptExecution
var b, c Script
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, scriptExecutionDBTypes, false, strmangle.SetComplement(scriptExecutionPrimaryKeyColumns, scriptExecutionColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
for i, x := range []*Script{&b, &c} {
err = a.SetScript(ctx, tx, i != 0, x)
if err != nil {
t.Fatal(err)
}
if a.R.Script != x {
t.Error("relationship struct not set to correct value")
}
if x.R.ScriptExecutions[0] != &a {
t.Error("failed to append to foreign relationship struct")
}
if !queries.Equal(a.ScriptID, x.ID) {
t.Error("foreign key was wrong value", a.ScriptID)
}
zero := reflect.Zero(reflect.TypeOf(a.ScriptID))
reflect.Indirect(reflect.ValueOf(&a.ScriptID)).Set(zero)
if err = a.Reload(ctx, tx); err != nil {
t.Fatal("failed to reload", err)
}
if !queries.Equal(a.ScriptID, x.ID) {
t.Error("foreign key was wrong value", a.ScriptID, x.ID)
}
}
}
func testScriptExecutionToOneRemoveOpScriptUsingScript(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a ScriptExecution
var b Script
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, scriptExecutionDBTypes, false, strmangle.SetComplement(scriptExecutionPrimaryKeyColumns, scriptExecutionColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = a.SetScript(ctx, tx, true, &b); err != nil {
t.Fatal(err)
}
if err = a.RemoveScript(ctx, tx, &b); err != nil {
t.Error("failed to remove relationship")
}
count, err := a.Script().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want no relationships remaining")
}
if a.R.Script != nil {
t.Error("R struct entry should be nil")
}
if !queries.IsValuerNil(a.ScriptID) {
t.Error("foreign key value should be nil")
}
if len(b.R.ScriptExecutions) != 0 {
t.Error("failed to remove a from b's relationships")
}
}
func testScriptExecutionsReload(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = o.Reload(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptExecutionsReloadAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptExecutionSlice{o}
if err = slice.ReloadAll(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptExecutionsSelect(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := ScriptExecutions().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 1 {
t.Error("want one record, got:", len(slice))
}
}
var (
scriptExecutionDBTypes = map[string]string{`ID`: `uuid`, `ScriptID`: `uuid`, `ExecutionType`: `character varying`, `ExecutionStatus`: `character varying`, `ExecutionTime`: `timestamp without time zone`}
_ = bytes.MinRead
)
func testScriptExecutionsUpdate(t *testing.T) {
t.Parallel()
if 0 == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with no primary key columns")
}
if len(scriptExecutionAllColumns) == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only affect one row but affected", rowsAff)
}
}
func testScriptExecutionsSliceUpdateAll(t *testing.T) {
t.Parallel()
if len(scriptExecutionAllColumns) == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
// Remove Primary keys and unique columns from what we plan to update
var fields []string
if strmangle.StringSliceMatch(scriptExecutionAllColumns, scriptExecutionPrimaryKeyColumns) {
fields = scriptExecutionAllColumns
} else {
fields = strmangle.SetComplement(
scriptExecutionAllColumns,
scriptExecutionPrimaryKeyColumns,
)
}
value := reflect.Indirect(reflect.ValueOf(o))
typ := reflect.TypeOf(o).Elem()
n := typ.NumField()
updateMap := M{}
for _, col := range fields {
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.Tag.Get("boil") == col {
updateMap[col] = value.Field(i).Interface()
}
}
}
slice := ScriptExecutionSlice{o}
if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("wanted one record updated but got", rowsAff)
}
}
func testScriptExecutionsUpsert(t *testing.T) {
t.Parallel()
if len(scriptExecutionAllColumns) == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
// Attempt the INSERT side of an UPSERT
o := ScriptExecution{}
if err = randomize.Struct(seed, &o, scriptExecutionDBTypes, true); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil {
t.Errorf("Unable to upsert ScriptExecution: %s", err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
// Attempt the UPDATE side of an UPSERT
if err = randomize.Struct(seed, &o, scriptExecutionDBTypes, false, scriptExecutionPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil {
t.Errorf("Unable to upsert ScriptExecution: %s", err)
}
count, err = ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,56 +13,84 @@ import "testing"
// Separating the tests thusly grants avoidance of Postgres deadlocks.
func TestParent(t *testing.T) {
t.Run("AuditEvents", testAuditEvents)
t.Run("Scripts", testScripts)
t.Run("ScriptExecutions", testScriptExecutions)
}
func TestDelete(t *testing.T) {
t.Run("AuditEvents", testAuditEventsDelete)
t.Run("Scripts", testScriptsDelete)
t.Run("ScriptExecutions", testScriptExecutionsDelete)
}
func TestQueryDeleteAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsQueryDeleteAll)
t.Run("Scripts", testScriptsQueryDeleteAll)
t.Run("ScriptExecutions", testScriptExecutionsQueryDeleteAll)
}
func TestSliceDeleteAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSliceDeleteAll)
t.Run("Scripts", testScriptsSliceDeleteAll)
t.Run("ScriptExecutions", testScriptExecutionsSliceDeleteAll)
}
func TestExists(t *testing.T) {
t.Run("AuditEvents", testAuditEventsExists)
t.Run("Scripts", testScriptsExists)
t.Run("ScriptExecutions", testScriptExecutionsExists)
}
func TestFind(t *testing.T) {
t.Run("AuditEvents", testAuditEventsFind)
t.Run("Scripts", testScriptsFind)
t.Run("ScriptExecutions", testScriptExecutionsFind)
}
func TestBind(t *testing.T) {
t.Run("AuditEvents", testAuditEventsBind)
t.Run("Scripts", testScriptsBind)
t.Run("ScriptExecutions", testScriptExecutionsBind)
}
func TestOne(t *testing.T) {
t.Run("AuditEvents", testAuditEventsOne)
t.Run("Scripts", testScriptsOne)
t.Run("ScriptExecutions", testScriptExecutionsOne)
}
func TestAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsAll)
t.Run("Scripts", testScriptsAll)
t.Run("ScriptExecutions", testScriptExecutionsAll)
}
func TestCount(t *testing.T) {
t.Run("AuditEvents", testAuditEventsCount)
t.Run("Scripts", testScriptsCount)
t.Run("ScriptExecutions", testScriptExecutionsCount)
}
func TestHooks(t *testing.T) {
t.Run("AuditEvents", testAuditEventsHooks)
t.Run("Scripts", testScriptsHooks)
t.Run("ScriptExecutions", testScriptExecutionsHooks)
}
func TestInsert(t *testing.T) {
t.Run("AuditEvents", testAuditEventsInsert)
t.Run("AuditEvents", testAuditEventsInsertWhitelist)
t.Run("Scripts", testScriptsInsert)
t.Run("Scripts", testScriptsInsertWhitelist)
t.Run("ScriptExecutions", testScriptExecutionsInsert)
t.Run("ScriptExecutions", testScriptExecutionsInsertWhitelist)
}
// TestToOne tests cannot be run in parallel
// or deadlocks can occur.
func TestToOne(t *testing.T) {}
func TestToOne(t *testing.T) {
t.Run("ScriptExecutionToScriptUsingScript", testScriptExecutionToOneScriptUsingScript)
}
// TestOneToOne tests cannot be run in parallel
// or deadlocks can occur.
@@ -70,11 +98,15 @@ func TestOneToOne(t *testing.T) {}
// TestToMany tests cannot be run in parallel
// or deadlocks can occur.
func TestToMany(t *testing.T) {}
func TestToMany(t *testing.T) {
t.Run("ScriptToScriptExecutions", testScriptToManyScriptExecutions)
}
// TestToOneSet tests cannot be run in parallel
// or deadlocks can occur.
func TestToOneSet(t *testing.T) {}
func TestToOneSet(t *testing.T) {
t.Run("ScriptExecutionToScriptUsingScriptExecutions", testScriptExecutionToOneSetOpScriptUsingScript)
}
// TestToOneRemove tests cannot be run in parallel
// or deadlocks can occur.
@@ -90,7 +122,9 @@ func TestOneToOneRemove(t *testing.T) {}
// TestToManyAdd tests cannot be run in parallel
// or deadlocks can occur.
func TestToManyAdd(t *testing.T) {}
func TestToManyAdd(t *testing.T) {
t.Run("ScriptToScriptExecutions", testScriptToManyAddOpScriptExecutions)
}
// TestToManySet tests cannot be run in parallel
// or deadlocks can occur.
@@ -102,20 +136,30 @@ func TestToManyRemove(t *testing.T) {}
func TestReload(t *testing.T) {
t.Run("AuditEvents", testAuditEventsReload)
t.Run("Scripts", testScriptsReload)
t.Run("ScriptExecutions", testScriptExecutionsReload)
}
func TestReloadAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsReloadAll)
t.Run("Scripts", testScriptsReloadAll)
t.Run("ScriptExecutions", testScriptExecutionsReloadAll)
}
func TestSelect(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSelect)
t.Run("Scripts", testScriptsSelect)
t.Run("ScriptExecutions", testScriptExecutionsSelect)
}
func TestUpdate(t *testing.T) {
t.Run("AuditEvents", testAuditEventsUpdate)
t.Run("Scripts", testScriptsUpdate)
t.Run("ScriptExecutions", testScriptExecutionsUpdate)
}
func TestSliceUpdateAll(t *testing.T) {
t.Run("AuditEvents", testAuditEventsSliceUpdateAll)
t.Run("Scripts", testScriptsSliceUpdateAll)
t.Run("ScriptExecutions", testScriptExecutionsSliceUpdateAll)
}

View File

@@ -4,7 +4,11 @@
package sqlite3
var TableNames = struct {
AuditEvent string
AuditEvent string
Script string
ScriptExecution string
}{
AuditEvent: "audit_event",
AuditEvent: "audit_event",
Script: "script",
ScriptExecution: "script_execution",
}

View File

@@ -0,0 +1,987 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
"sync"
"time"
"github.com/pkg/errors"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/queries/qm"
"github.com/thrasher-corp/sqlboiler/queries/qmhelper"
"github.com/thrasher-corp/sqlboiler/strmangle"
"github.com/volatiletech/null"
)
// Script is an object representing the database table.
type Script struct {
ID string `boil:"id" json:"id" toml:"id" yaml:"id"`
ScriptID string `boil:"script_id" json:"script_id" toml:"script_id" yaml:"script_id"`
ScriptName string `boil:"script_name" json:"script_name" toml:"script_name" yaml:"script_name"`
ScriptPath string `boil:"script_path" json:"script_path" toml:"script_path" yaml:"script_path"`
ScriptData null.Bytes `boil:"script_data" json:"script_data,omitempty" toml:"script_data" yaml:"script_data,omitempty"`
LastExecutedAt string `boil:"last_executed_at" json:"last_executed_at" toml:"last_executed_at" yaml:"last_executed_at"`
CreatedAt string `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
R *scriptR `boil:"-" json:"-" toml:"-" yaml:"-"`
L scriptL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
var ScriptColumns = struct {
ID string
ScriptID string
ScriptName string
ScriptPath string
ScriptData string
LastExecutedAt string
CreatedAt string
}{
ID: "id",
ScriptID: "script_id",
ScriptName: "script_name",
ScriptPath: "script_path",
ScriptData: "script_data",
LastExecutedAt: "last_executed_at",
CreatedAt: "created_at",
}
// Generated where
type whereHelpernull_Bytes struct{ field string }
func (w whereHelpernull_Bytes) EQ(x null.Bytes) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, false, x)
}
func (w whereHelpernull_Bytes) NEQ(x null.Bytes) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, true, x)
}
func (w whereHelpernull_Bytes) IsNull() qm.QueryMod { return qmhelper.WhereIsNull(w.field) }
func (w whereHelpernull_Bytes) IsNotNull() qm.QueryMod { return qmhelper.WhereIsNotNull(w.field) }
func (w whereHelpernull_Bytes) LT(x null.Bytes) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LT, x)
}
func (w whereHelpernull_Bytes) LTE(x null.Bytes) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LTE, x)
}
func (w whereHelpernull_Bytes) GT(x null.Bytes) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GT, x)
}
func (w whereHelpernull_Bytes) GTE(x null.Bytes) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GTE, x)
}
var ScriptWhere = struct {
ID whereHelperstring
ScriptID whereHelperstring
ScriptName whereHelperstring
ScriptPath whereHelperstring
ScriptData whereHelpernull_Bytes
LastExecutedAt whereHelperstring
CreatedAt whereHelperstring
}{
ID: whereHelperstring{field: "\"script\".\"id\""},
ScriptID: whereHelperstring{field: "\"script\".\"script_id\""},
ScriptName: whereHelperstring{field: "\"script\".\"script_name\""},
ScriptPath: whereHelperstring{field: "\"script\".\"script_path\""},
ScriptData: whereHelpernull_Bytes{field: "\"script\".\"script_data\""},
LastExecutedAt: whereHelperstring{field: "\"script\".\"last_executed_at\""},
CreatedAt: whereHelperstring{field: "\"script\".\"created_at\""},
}
// ScriptRels is where relationship names are stored.
var ScriptRels = struct {
ScriptExecutions string
}{
ScriptExecutions: "ScriptExecutions",
}
// scriptR is where relationships are stored.
type scriptR struct {
ScriptExecutions ScriptExecutionSlice
}
// NewStruct creates a new relationship struct
func (*scriptR) NewStruct() *scriptR {
return &scriptR{}
}
// scriptL is where Load methods for each relationship are stored.
type scriptL struct{}
var (
scriptAllColumns = []string{"id", "script_id", "script_name", "script_path", "script_data", "last_executed_at", "created_at"}
scriptColumnsWithoutDefault = []string{"id", "script_id", "script_name", "script_path", "script_data"}
scriptColumnsWithDefault = []string{"last_executed_at", "created_at"}
scriptPrimaryKeyColumns = []string{"id"}
)
type (
// ScriptSlice is an alias for a slice of pointers to Script.
// This should generally be used opposed to []Script.
ScriptSlice []*Script
// ScriptHook is the signature for custom Script hook methods
ScriptHook func(context.Context, boil.ContextExecutor, *Script) error
scriptQuery struct {
*queries.Query
}
)
// Cache for insert, update and upsert
var (
scriptType = reflect.TypeOf(&Script{})
scriptMapping = queries.MakeStructMapping(scriptType)
scriptPrimaryKeyMapping, _ = queries.BindMapping(scriptType, scriptMapping, scriptPrimaryKeyColumns)
scriptInsertCacheMut sync.RWMutex
scriptInsertCache = make(map[string]insertCache)
scriptUpdateCacheMut sync.RWMutex
scriptUpdateCache = make(map[string]updateCache)
scriptUpsertCacheMut sync.RWMutex
scriptUpsertCache = make(map[string]insertCache)
)
var (
// Force time package dependency for automated UpdatedAt/CreatedAt.
_ = time.Second
// Force qmhelper dependency for where clause generation (which doesn't
// always happen)
_ = qmhelper.Where
)
var scriptBeforeInsertHooks []ScriptHook
var scriptBeforeUpdateHooks []ScriptHook
var scriptBeforeDeleteHooks []ScriptHook
var scriptBeforeUpsertHooks []ScriptHook
var scriptAfterInsertHooks []ScriptHook
var scriptAfterSelectHooks []ScriptHook
var scriptAfterUpdateHooks []ScriptHook
var scriptAfterDeleteHooks []ScriptHook
var scriptAfterUpsertHooks []ScriptHook
// doBeforeInsertHooks executes all "before insert" hooks.
func (o *Script) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptBeforeInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpdateHooks executes all "before Update" hooks.
func (o *Script) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptBeforeUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeDeleteHooks executes all "before Delete" hooks.
func (o *Script) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptBeforeDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpsertHooks executes all "before Upsert" hooks.
func (o *Script) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptBeforeUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterInsertHooks executes all "after Insert" hooks.
func (o *Script) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptAfterInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterSelectHooks executes all "after Select" hooks.
func (o *Script) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptAfterSelectHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpdateHooks executes all "after Update" hooks.
func (o *Script) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptAfterUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterDeleteHooks executes all "after Delete" hooks.
func (o *Script) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptAfterDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpsertHooks executes all "after Upsert" hooks.
func (o *Script) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptAfterUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// AddScriptHook registers your hook function for all future operations.
func AddScriptHook(hookPoint boil.HookPoint, scriptHook ScriptHook) {
switch hookPoint {
case boil.BeforeInsertHook:
scriptBeforeInsertHooks = append(scriptBeforeInsertHooks, scriptHook)
case boil.BeforeUpdateHook:
scriptBeforeUpdateHooks = append(scriptBeforeUpdateHooks, scriptHook)
case boil.BeforeDeleteHook:
scriptBeforeDeleteHooks = append(scriptBeforeDeleteHooks, scriptHook)
case boil.BeforeUpsertHook:
scriptBeforeUpsertHooks = append(scriptBeforeUpsertHooks, scriptHook)
case boil.AfterInsertHook:
scriptAfterInsertHooks = append(scriptAfterInsertHooks, scriptHook)
case boil.AfterSelectHook:
scriptAfterSelectHooks = append(scriptAfterSelectHooks, scriptHook)
case boil.AfterUpdateHook:
scriptAfterUpdateHooks = append(scriptAfterUpdateHooks, scriptHook)
case boil.AfterDeleteHook:
scriptAfterDeleteHooks = append(scriptAfterDeleteHooks, scriptHook)
case boil.AfterUpsertHook:
scriptAfterUpsertHooks = append(scriptAfterUpsertHooks, scriptHook)
}
}
// One returns a single script record from the query.
func (q scriptQuery) One(ctx context.Context, exec boil.ContextExecutor) (*Script, error) {
o := &Script{}
queries.SetLimit(q.Query, 1)
err := q.Bind(ctx, exec, o)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: failed to execute a one query for script")
}
if err := o.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
return o, nil
}
// All returns all Script records from the query.
func (q scriptQuery) All(ctx context.Context, exec boil.ContextExecutor) (ScriptSlice, error) {
var o []*Script
err := q.Bind(ctx, exec, &o)
if err != nil {
return nil, errors.Wrap(err, "sqlite3: failed to assign all query results to Script slice")
}
if len(scriptAfterSelectHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
}
}
return o, nil
}
// Count returns the count of all Script records in the query.
func (q scriptQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to count script rows")
}
return count, nil
}
// Exists checks if the row exists in the table.
func (q scriptQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
queries.SetLimit(q.Query, 1)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return false, errors.Wrap(err, "sqlite3: failed to check if script exists")
}
return count > 0, nil
}
// ScriptExecutions retrieves all the script_execution's ScriptExecutions with an executor.
func (o *Script) ScriptExecutions(mods ...qm.QueryMod) scriptExecutionQuery {
var queryMods []qm.QueryMod
if len(mods) != 0 {
queryMods = append(queryMods, mods...)
}
queryMods = append(queryMods,
qm.Where("\"script_execution\".\"script_id\"=?", o.ID),
)
query := ScriptExecutions(queryMods...)
queries.SetFrom(query.Query, "\"script_execution\"")
if len(queries.GetSelect(query.Query)) == 0 {
queries.SetSelect(query.Query, []string{"\"script_execution\".*"})
}
return query
}
// LoadScriptExecutions allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-M or N-M relationship.
func (scriptL) LoadScriptExecutions(ctx context.Context, e boil.ContextExecutor, singular bool, maybeScript interface{}, mods queries.Applicator) error {
var slice []*Script
var object *Script
if singular {
object = maybeScript.(*Script)
} else {
slice = *maybeScript.(*[]*Script)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &scriptR{}
}
args = append(args, object.ID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &scriptR{}
}
for _, a := range args {
if a == obj.ID {
continue Outer
}
}
args = append(args, obj.ID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(qm.From(`script_execution`), qm.WhereIn(`script_execution.script_id in ?`, args...))
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load script_execution")
}
var resultSlice []*ScriptExecution
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice script_execution")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results in eager load on script_execution")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for script_execution")
}
if len(scriptExecutionAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if singular {
object.R.ScriptExecutions = resultSlice
for _, foreign := range resultSlice {
if foreign.R == nil {
foreign.R = &scriptExecutionR{}
}
foreign.R.Script = object
}
return nil
}
for _, foreign := range resultSlice {
for _, local := range slice {
if local.ID == foreign.ScriptID {
local.R.ScriptExecutions = append(local.R.ScriptExecutions, foreign)
if foreign.R == nil {
foreign.R = &scriptExecutionR{}
}
foreign.R.Script = local
break
}
}
}
return nil
}
// AddScriptExecutions adds the given related objects to the existing relationships
// of the script, optionally inserting them as new records.
// Appends related to o.R.ScriptExecutions.
// Sets related.R.Script appropriately.
func (o *Script) AddScriptExecutions(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*ScriptExecution) error {
var err error
for _, rel := range related {
if insert {
rel.ScriptID = o.ID
if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
} else {
updateQuery := fmt.Sprintf(
"UPDATE \"script_execution\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"script_id"}),
strmangle.WhereClause("\"", "\"", 0, scriptExecutionPrimaryKeyColumns),
)
values := []interface{}{o.ID, rel.ID}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update foreign table")
}
rel.ScriptID = o.ID
}
}
if o.R == nil {
o.R = &scriptR{
ScriptExecutions: related,
}
} else {
o.R.ScriptExecutions = append(o.R.ScriptExecutions, related...)
}
for _, rel := range related {
if rel.R == nil {
rel.R = &scriptExecutionR{
Script: o,
}
} else {
rel.R.Script = o
}
}
return nil
}
// Scripts retrieves all the records using an executor.
func Scripts(mods ...qm.QueryMod) scriptQuery {
mods = append(mods, qm.From("\"script\""))
return scriptQuery{NewQuery(mods...)}
}
// FindScript retrieves a single record by ID with an executor.
// If selectCols is empty Find will return all columns.
func FindScript(ctx context.Context, exec boil.ContextExecutor, iD string, selectCols ...string) (*Script, error) {
scriptObj := &Script{}
sel := "*"
if len(selectCols) > 0 {
sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
}
query := fmt.Sprintf(
"select %s from \"script\" where \"id\"=?", sel,
)
q := queries.Raw(query, iD)
err := q.Bind(ctx, exec, scriptObj)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: unable to select from script")
}
return scriptObj, nil
}
// Insert a single record using an executor.
// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
func (o *Script) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
if o == nil {
return errors.New("sqlite3: no script provided for insertion")
}
var err error
if err := o.doBeforeInsertHooks(ctx, exec); err != nil {
return err
}
nzDefaults := queries.NonZeroDefaultSet(scriptColumnsWithDefault, o)
key := makeCacheKey(columns, nzDefaults)
scriptInsertCacheMut.RLock()
cache, cached := scriptInsertCache[key]
scriptInsertCacheMut.RUnlock()
if !cached {
wl, returnColumns := columns.InsertColumnSet(
scriptAllColumns,
scriptColumnsWithDefault,
scriptColumnsWithoutDefault,
nzDefaults,
)
cache.valueMapping, err = queries.BindMapping(scriptType, scriptMapping, wl)
if err != nil {
return err
}
cache.retMapping, err = queries.BindMapping(scriptType, scriptMapping, returnColumns)
if err != nil {
return err
}
if len(wl) != 0 {
cache.query = fmt.Sprintf("INSERT INTO \"script\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
} else {
cache.query = "INSERT INTO \"script\" () VALUES ()%s%s"
}
var queryOutput, queryReturning string
if len(cache.retMapping) != 0 {
cache.retQuery = fmt.Sprintf("SELECT \"%s\" FROM \"script\" WHERE %s", strings.Join(returnColumns, "\",\""), strmangle.WhereClause("\"", "\"", 0, scriptPrimaryKeyColumns))
}
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
}
value := reflect.Indirect(reflect.ValueOf(o))
vals := queries.ValuesFromMapping(value, cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, vals)
}
_, err = exec.ExecContext(ctx, cache.query, vals...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to insert into script")
}
var identifierCols []interface{}
if len(cache.retMapping) == 0 {
goto CacheNoHooks
}
identifierCols = []interface{}{
o.ID,
}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.retQuery)
fmt.Fprintln(boil.DebugWriter, identifierCols...)
}
err = exec.QueryRowContext(ctx, cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to populate default values for script")
}
CacheNoHooks:
if !cached {
scriptInsertCacheMut.Lock()
scriptInsertCache[key] = cache
scriptInsertCacheMut.Unlock()
}
return o.doAfterInsertHooks(ctx, exec)
}
// Update uses an executor to update the Script.
// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
func (o *Script) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
var err error
if err = o.doBeforeUpdateHooks(ctx, exec); err != nil {
return 0, err
}
key := makeCacheKey(columns, nil)
scriptUpdateCacheMut.RLock()
cache, cached := scriptUpdateCache[key]
scriptUpdateCacheMut.RUnlock()
if !cached {
wl := columns.UpdateColumnSet(
scriptAllColumns,
scriptPrimaryKeyColumns,
)
if len(wl) == 0 {
return 0, errors.New("sqlite3: unable to update script, could not build whitelist")
}
cache.query = fmt.Sprintf("UPDATE \"script\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, wl),
strmangle.WhereClause("\"", "\"", 0, scriptPrimaryKeyColumns),
)
cache.valueMapping, err = queries.BindMapping(scriptType, scriptMapping, append(wl, scriptPrimaryKeyColumns...))
if err != nil {
return 0, err
}
}
values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, values)
}
var result sql.Result
result, err = exec.ExecContext(ctx, cache.query, values...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update script row")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by update for script")
}
if !cached {
scriptUpdateCacheMut.Lock()
scriptUpdateCache[key] = cache
scriptUpdateCacheMut.Unlock()
}
return rowsAff, o.doAfterUpdateHooks(ctx, exec)
}
// UpdateAll updates all rows with the specified column values.
func (q scriptQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
queries.SetUpdate(q.Query, cols)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all for script")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected for script")
}
return rowsAff, nil
}
// UpdateAll updates all rows with the specified column values, using an executor.
func (o ScriptSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
ln := int64(len(o))
if ln == 0 {
return 0, nil
}
if len(cols) == 0 {
return 0, errors.New("sqlite3: update all requires at least one column argument")
}
colNames := make([]string, len(cols))
args := make([]interface{}, len(cols))
i := 0
for name, value := range cols {
colNames[i] = name
args[i] = value
i++
}
// Append all of the primary key values for each column
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := fmt.Sprintf("UPDATE \"script\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, colNames),
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptPrimaryKeyColumns, len(o)))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all in script slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected all in update all script")
}
return rowsAff, nil
}
// Delete deletes a single Script record with an executor.
// Delete will match against the primary key column to find the record to delete.
func (o *Script) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if o == nil {
return 0, errors.New("sqlite3: no Script provided for delete")
}
if err := o.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), scriptPrimaryKeyMapping)
sql := "DELETE FROM \"script\" WHERE \"id\"=?"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete from script")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by delete for script")
}
if err := o.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
return rowsAff, nil
}
// DeleteAll deletes all matching rows.
func (q scriptQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if q.Query == nil {
return 0, errors.New("sqlite3: no scriptQuery provided for delete all")
}
queries.SetDelete(q.Query)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from script")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for script")
}
return rowsAff, nil
}
// DeleteAll deletes all rows in the slice, using an executor.
func (o ScriptSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if len(o) == 0 {
return 0, nil
}
if len(scriptBeforeDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
var args []interface{}
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "DELETE FROM \"script\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptPrimaryKeyColumns, len(o))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from script slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for script")
}
if len(scriptAfterDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
return rowsAff, nil
}
// Reload refetches the object from the database
// using the primary keys with an executor.
func (o *Script) Reload(ctx context.Context, exec boil.ContextExecutor) error {
ret, err := FindScript(ctx, exec, o.ID)
if err != nil {
return err
}
*o = *ret
return nil
}
// ReloadAll refetches every row with matching primary key column values
// and overwrites the original object slice with the newly updated slice.
func (o *ScriptSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
if o == nil || len(*o) == 0 {
return nil
}
slice := ScriptSlice{}
var args []interface{}
for _, obj := range *o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "SELECT \"script\".* FROM \"script\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptPrimaryKeyColumns, len(*o))
q := queries.Raw(sql, args...)
err := q.Bind(ctx, exec, &slice)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to reload all in ScriptSlice")
}
*o = slice
return nil
}
// ScriptExists checks if the Script row exists.
func ScriptExists(ctx context.Context, exec boil.ContextExecutor, iD string) (bool, error) {
var exists bool
sql := "select exists(select 1 from \"script\" where \"id\"=? limit 1)"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, iD)
}
row := exec.QueryRowContext(ctx, sql, iD)
err := row.Scan(&exists)
if err != nil {
return false, errors.Wrap(err, "sqlite3: unable to check if script exists")
}
return exists, nil
}

View File

@@ -0,0 +1,957 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
"sync"
"time"
"github.com/pkg/errors"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/queries/qm"
"github.com/thrasher-corp/sqlboiler/queries/qmhelper"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
// ScriptExecution is an object representing the database table.
type ScriptExecution struct {
ID int64 `boil:"id" json:"id" toml:"id" yaml:"id"`
ScriptID string `boil:"script_id" json:"script_id" toml:"script_id" yaml:"script_id"`
ExecutionType string `boil:"execution_type" json:"execution_type" toml:"execution_type" yaml:"execution_type"`
ExecutionStatus string `boil:"execution_status" json:"execution_status" toml:"execution_status" yaml:"execution_status"`
ExecutionTime string `boil:"execution_time" json:"execution_time" toml:"execution_time" yaml:"execution_time"`
R *scriptExecutionR `boil:"-" json:"-" toml:"-" yaml:"-"`
L scriptExecutionL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
var ScriptExecutionColumns = struct {
ID string
ScriptID string
ExecutionType string
ExecutionStatus string
ExecutionTime string
}{
ID: "id",
ScriptID: "script_id",
ExecutionType: "execution_type",
ExecutionStatus: "execution_status",
ExecutionTime: "execution_time",
}
// Generated where
var ScriptExecutionWhere = struct {
ID whereHelperint64
ScriptID whereHelperstring
ExecutionType whereHelperstring
ExecutionStatus whereHelperstring
ExecutionTime whereHelperstring
}{
ID: whereHelperint64{field: "\"script_execution\".\"id\""},
ScriptID: whereHelperstring{field: "\"script_execution\".\"script_id\""},
ExecutionType: whereHelperstring{field: "\"script_execution\".\"execution_type\""},
ExecutionStatus: whereHelperstring{field: "\"script_execution\".\"execution_status\""},
ExecutionTime: whereHelperstring{field: "\"script_execution\".\"execution_time\""},
}
// ScriptExecutionRels is where relationship names are stored.
var ScriptExecutionRels = struct {
Script string
}{
Script: "Script",
}
// scriptExecutionR is where relationships are stored.
type scriptExecutionR struct {
Script *Script
}
// NewStruct creates a new relationship struct
func (*scriptExecutionR) NewStruct() *scriptExecutionR {
return &scriptExecutionR{}
}
// scriptExecutionL is where Load methods for each relationship are stored.
type scriptExecutionL struct{}
var (
scriptExecutionAllColumns = []string{"id", "script_id", "execution_type", "execution_status", "execution_time"}
scriptExecutionColumnsWithoutDefault = []string{"script_id", "execution_type", "execution_status"}
scriptExecutionColumnsWithDefault = []string{"id", "execution_time"}
scriptExecutionPrimaryKeyColumns = []string{"id"}
)
type (
// ScriptExecutionSlice is an alias for a slice of pointers to ScriptExecution.
// This should generally be used opposed to []ScriptExecution.
ScriptExecutionSlice []*ScriptExecution
// ScriptExecutionHook is the signature for custom ScriptExecution hook methods
ScriptExecutionHook func(context.Context, boil.ContextExecutor, *ScriptExecution) error
scriptExecutionQuery struct {
*queries.Query
}
)
// Cache for insert, update and upsert
var (
scriptExecutionType = reflect.TypeOf(&ScriptExecution{})
scriptExecutionMapping = queries.MakeStructMapping(scriptExecutionType)
scriptExecutionPrimaryKeyMapping, _ = queries.BindMapping(scriptExecutionType, scriptExecutionMapping, scriptExecutionPrimaryKeyColumns)
scriptExecutionInsertCacheMut sync.RWMutex
scriptExecutionInsertCache = make(map[string]insertCache)
scriptExecutionUpdateCacheMut sync.RWMutex
scriptExecutionUpdateCache = make(map[string]updateCache)
scriptExecutionUpsertCacheMut sync.RWMutex
scriptExecutionUpsertCache = make(map[string]insertCache)
)
var (
// Force time package dependency for automated UpdatedAt/CreatedAt.
_ = time.Second
// Force qmhelper dependency for where clause generation (which doesn't
// always happen)
_ = qmhelper.Where
)
var scriptExecutionBeforeInsertHooks []ScriptExecutionHook
var scriptExecutionBeforeUpdateHooks []ScriptExecutionHook
var scriptExecutionBeforeDeleteHooks []ScriptExecutionHook
var scriptExecutionBeforeUpsertHooks []ScriptExecutionHook
var scriptExecutionAfterInsertHooks []ScriptExecutionHook
var scriptExecutionAfterSelectHooks []ScriptExecutionHook
var scriptExecutionAfterUpdateHooks []ScriptExecutionHook
var scriptExecutionAfterDeleteHooks []ScriptExecutionHook
var scriptExecutionAfterUpsertHooks []ScriptExecutionHook
// doBeforeInsertHooks executes all "before insert" hooks.
func (o *ScriptExecution) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionBeforeInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpdateHooks executes all "before Update" hooks.
func (o *ScriptExecution) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionBeforeUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeDeleteHooks executes all "before Delete" hooks.
func (o *ScriptExecution) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionBeforeDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpsertHooks executes all "before Upsert" hooks.
func (o *ScriptExecution) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionBeforeUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterInsertHooks executes all "after Insert" hooks.
func (o *ScriptExecution) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionAfterInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterSelectHooks executes all "after Select" hooks.
func (o *ScriptExecution) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionAfterSelectHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpdateHooks executes all "after Update" hooks.
func (o *ScriptExecution) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionAfterUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterDeleteHooks executes all "after Delete" hooks.
func (o *ScriptExecution) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionAfterDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpsertHooks executes all "after Upsert" hooks.
func (o *ScriptExecution) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range scriptExecutionAfterUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// AddScriptExecutionHook registers your hook function for all future operations.
func AddScriptExecutionHook(hookPoint boil.HookPoint, scriptExecutionHook ScriptExecutionHook) {
switch hookPoint {
case boil.BeforeInsertHook:
scriptExecutionBeforeInsertHooks = append(scriptExecutionBeforeInsertHooks, scriptExecutionHook)
case boil.BeforeUpdateHook:
scriptExecutionBeforeUpdateHooks = append(scriptExecutionBeforeUpdateHooks, scriptExecutionHook)
case boil.BeforeDeleteHook:
scriptExecutionBeforeDeleteHooks = append(scriptExecutionBeforeDeleteHooks, scriptExecutionHook)
case boil.BeforeUpsertHook:
scriptExecutionBeforeUpsertHooks = append(scriptExecutionBeforeUpsertHooks, scriptExecutionHook)
case boil.AfterInsertHook:
scriptExecutionAfterInsertHooks = append(scriptExecutionAfterInsertHooks, scriptExecutionHook)
case boil.AfterSelectHook:
scriptExecutionAfterSelectHooks = append(scriptExecutionAfterSelectHooks, scriptExecutionHook)
case boil.AfterUpdateHook:
scriptExecutionAfterUpdateHooks = append(scriptExecutionAfterUpdateHooks, scriptExecutionHook)
case boil.AfterDeleteHook:
scriptExecutionAfterDeleteHooks = append(scriptExecutionAfterDeleteHooks, scriptExecutionHook)
case boil.AfterUpsertHook:
scriptExecutionAfterUpsertHooks = append(scriptExecutionAfterUpsertHooks, scriptExecutionHook)
}
}
// One returns a single scriptExecution record from the query.
func (q scriptExecutionQuery) One(ctx context.Context, exec boil.ContextExecutor) (*ScriptExecution, error) {
o := &ScriptExecution{}
queries.SetLimit(q.Query, 1)
err := q.Bind(ctx, exec, o)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: failed to execute a one query for script_execution")
}
if err := o.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
return o, nil
}
// All returns all ScriptExecution records from the query.
func (q scriptExecutionQuery) All(ctx context.Context, exec boil.ContextExecutor) (ScriptExecutionSlice, error) {
var o []*ScriptExecution
err := q.Bind(ctx, exec, &o)
if err != nil {
return nil, errors.Wrap(err, "sqlite3: failed to assign all query results to ScriptExecution slice")
}
if len(scriptExecutionAfterSelectHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
}
}
return o, nil
}
// Count returns the count of all ScriptExecution records in the query.
func (q scriptExecutionQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to count script_execution rows")
}
return count, nil
}
// Exists checks if the row exists in the table.
func (q scriptExecutionQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
queries.SetLimit(q.Query, 1)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return false, errors.Wrap(err, "sqlite3: failed to check if script_execution exists")
}
return count > 0, nil
}
// Script pointed to by the foreign key.
func (o *ScriptExecution) Script(mods ...qm.QueryMod) scriptQuery {
queryMods := []qm.QueryMod{
qm.Where("\"id\" = ?", o.ScriptID),
}
queryMods = append(queryMods, mods...)
query := Scripts(queryMods...)
queries.SetFrom(query.Query, "\"script\"")
return query
}
// LoadScript allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for an N-1 relationship.
func (scriptExecutionL) LoadScript(ctx context.Context, e boil.ContextExecutor, singular bool, maybeScriptExecution interface{}, mods queries.Applicator) error {
var slice []*ScriptExecution
var object *ScriptExecution
if singular {
object = maybeScriptExecution.(*ScriptExecution)
} else {
slice = *maybeScriptExecution.(*[]*ScriptExecution)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &scriptExecutionR{}
}
args = append(args, object.ScriptID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &scriptExecutionR{}
}
for _, a := range args {
if a == obj.ScriptID {
continue Outer
}
}
args = append(args, obj.ScriptID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(qm.From(`script`), qm.WhereIn(`script.id in ?`, args...))
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load Script")
}
var resultSlice []*Script
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice Script")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results of eager load for script")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for script")
}
if len(scriptExecutionAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if len(resultSlice) == 0 {
return nil
}
if singular {
foreign := resultSlice[0]
object.R.Script = foreign
if foreign.R == nil {
foreign.R = &scriptR{}
}
foreign.R.ScriptExecutions = append(foreign.R.ScriptExecutions, object)
return nil
}
for _, local := range slice {
for _, foreign := range resultSlice {
if local.ScriptID == foreign.ID {
local.R.Script = foreign
if foreign.R == nil {
foreign.R = &scriptR{}
}
foreign.R.ScriptExecutions = append(foreign.R.ScriptExecutions, local)
break
}
}
}
return nil
}
// SetScript of the scriptExecution to the related item.
// Sets o.R.Script to related.
// Adds o to related.R.ScriptExecutions.
func (o *ScriptExecution) SetScript(ctx context.Context, exec boil.ContextExecutor, insert bool, related *Script) error {
var err error
if insert {
if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}
updateQuery := fmt.Sprintf(
"UPDATE \"script_execution\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"script_id"}),
strmangle.WhereClause("\"", "\"", 0, scriptExecutionPrimaryKeyColumns),
)
values := []interface{}{related.ID, o.ID}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update local table")
}
o.ScriptID = related.ID
if o.R == nil {
o.R = &scriptExecutionR{
Script: related,
}
} else {
o.R.Script = related
}
if related.R == nil {
related.R = &scriptR{
ScriptExecutions: ScriptExecutionSlice{o},
}
} else {
related.R.ScriptExecutions = append(related.R.ScriptExecutions, o)
}
return nil
}
// ScriptExecutions retrieves all the records using an executor.
func ScriptExecutions(mods ...qm.QueryMod) scriptExecutionQuery {
mods = append(mods, qm.From("\"script_execution\""))
return scriptExecutionQuery{NewQuery(mods...)}
}
// FindScriptExecution retrieves a single record by ID with an executor.
// If selectCols is empty Find will return all columns.
func FindScriptExecution(ctx context.Context, exec boil.ContextExecutor, iD int64, selectCols ...string) (*ScriptExecution, error) {
scriptExecutionObj := &ScriptExecution{}
sel := "*"
if len(selectCols) > 0 {
sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
}
query := fmt.Sprintf(
"select %s from \"script_execution\" where \"id\"=?", sel,
)
q := queries.Raw(query, iD)
err := q.Bind(ctx, exec, scriptExecutionObj)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: unable to select from script_execution")
}
return scriptExecutionObj, nil
}
// Insert a single record using an executor.
// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
func (o *ScriptExecution) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
if o == nil {
return errors.New("sqlite3: no script_execution provided for insertion")
}
var err error
if err := o.doBeforeInsertHooks(ctx, exec); err != nil {
return err
}
nzDefaults := queries.NonZeroDefaultSet(scriptExecutionColumnsWithDefault, o)
key := makeCacheKey(columns, nzDefaults)
scriptExecutionInsertCacheMut.RLock()
cache, cached := scriptExecutionInsertCache[key]
scriptExecutionInsertCacheMut.RUnlock()
if !cached {
wl, returnColumns := columns.InsertColumnSet(
scriptExecutionAllColumns,
scriptExecutionColumnsWithDefault,
scriptExecutionColumnsWithoutDefault,
nzDefaults,
)
cache.valueMapping, err = queries.BindMapping(scriptExecutionType, scriptExecutionMapping, wl)
if err != nil {
return err
}
cache.retMapping, err = queries.BindMapping(scriptExecutionType, scriptExecutionMapping, returnColumns)
if err != nil {
return err
}
if len(wl) != 0 {
cache.query = fmt.Sprintf("INSERT INTO \"script_execution\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
} else {
cache.query = "INSERT INTO \"script_execution\" () VALUES ()%s%s"
}
var queryOutput, queryReturning string
if len(cache.retMapping) != 0 {
cache.retQuery = fmt.Sprintf("SELECT \"%s\" FROM \"script_execution\" WHERE %s", strings.Join(returnColumns, "\",\""), strmangle.WhereClause("\"", "\"", 0, scriptExecutionPrimaryKeyColumns))
}
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
}
value := reflect.Indirect(reflect.ValueOf(o))
vals := queries.ValuesFromMapping(value, cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, vals)
}
result, err := exec.ExecContext(ctx, cache.query, vals...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to insert into script_execution")
}
var lastID int64
var identifierCols []interface{}
if len(cache.retMapping) == 0 {
goto CacheNoHooks
}
lastID, err = result.LastInsertId()
if err != nil {
return ErrSyncFail
}
o.ID = int64(lastID)
if lastID != 0 && len(cache.retMapping) == 1 && cache.retMapping[0] == scriptExecutionMapping["ID"] {
goto CacheNoHooks
}
identifierCols = []interface{}{
o.ID,
}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.retQuery)
fmt.Fprintln(boil.DebugWriter, identifierCols...)
}
err = exec.QueryRowContext(ctx, cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to populate default values for script_execution")
}
CacheNoHooks:
if !cached {
scriptExecutionInsertCacheMut.Lock()
scriptExecutionInsertCache[key] = cache
scriptExecutionInsertCacheMut.Unlock()
}
return o.doAfterInsertHooks(ctx, exec)
}
// Update uses an executor to update the ScriptExecution.
// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
func (o *ScriptExecution) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
var err error
if err = o.doBeforeUpdateHooks(ctx, exec); err != nil {
return 0, err
}
key := makeCacheKey(columns, nil)
scriptExecutionUpdateCacheMut.RLock()
cache, cached := scriptExecutionUpdateCache[key]
scriptExecutionUpdateCacheMut.RUnlock()
if !cached {
wl := columns.UpdateColumnSet(
scriptExecutionAllColumns,
scriptExecutionPrimaryKeyColumns,
)
if len(wl) == 0 {
return 0, errors.New("sqlite3: unable to update script_execution, could not build whitelist")
}
cache.query = fmt.Sprintf("UPDATE \"script_execution\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, wl),
strmangle.WhereClause("\"", "\"", 0, scriptExecutionPrimaryKeyColumns),
)
cache.valueMapping, err = queries.BindMapping(scriptExecutionType, scriptExecutionMapping, append(wl, scriptExecutionPrimaryKeyColumns...))
if err != nil {
return 0, err
}
}
values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, values)
}
var result sql.Result
result, err = exec.ExecContext(ctx, cache.query, values...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update script_execution row")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by update for script_execution")
}
if !cached {
scriptExecutionUpdateCacheMut.Lock()
scriptExecutionUpdateCache[key] = cache
scriptExecutionUpdateCacheMut.Unlock()
}
return rowsAff, o.doAfterUpdateHooks(ctx, exec)
}
// UpdateAll updates all rows with the specified column values.
func (q scriptExecutionQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
queries.SetUpdate(q.Query, cols)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all for script_execution")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected for script_execution")
}
return rowsAff, nil
}
// UpdateAll updates all rows with the specified column values, using an executor.
func (o ScriptExecutionSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
ln := int64(len(o))
if ln == 0 {
return 0, nil
}
if len(cols) == 0 {
return 0, errors.New("sqlite3: update all requires at least one column argument")
}
colNames := make([]string, len(cols))
args := make([]interface{}, len(cols))
i := 0
for name, value := range cols {
colNames[i] = name
args[i] = value
i++
}
// Append all of the primary key values for each column
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptExecutionPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := fmt.Sprintf("UPDATE \"script_execution\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, colNames),
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptExecutionPrimaryKeyColumns, len(o)))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all in scriptExecution slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected all in update all scriptExecution")
}
return rowsAff, nil
}
// Delete deletes a single ScriptExecution record with an executor.
// Delete will match against the primary key column to find the record to delete.
func (o *ScriptExecution) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if o == nil {
return 0, errors.New("sqlite3: no ScriptExecution provided for delete")
}
if err := o.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), scriptExecutionPrimaryKeyMapping)
sql := "DELETE FROM \"script_execution\" WHERE \"id\"=?"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete from script_execution")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by delete for script_execution")
}
if err := o.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
return rowsAff, nil
}
// DeleteAll deletes all matching rows.
func (q scriptExecutionQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if q.Query == nil {
return 0, errors.New("sqlite3: no scriptExecutionQuery provided for delete all")
}
queries.SetDelete(q.Query)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from script_execution")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for script_execution")
}
return rowsAff, nil
}
// DeleteAll deletes all rows in the slice, using an executor.
func (o ScriptExecutionSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if len(o) == 0 {
return 0, nil
}
if len(scriptExecutionBeforeDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
var args []interface{}
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptExecutionPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "DELETE FROM \"script_execution\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptExecutionPrimaryKeyColumns, len(o))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from scriptExecution slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for script_execution")
}
if len(scriptExecutionAfterDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
return rowsAff, nil
}
// Reload refetches the object from the database
// using the primary keys with an executor.
func (o *ScriptExecution) Reload(ctx context.Context, exec boil.ContextExecutor) error {
ret, err := FindScriptExecution(ctx, exec, o.ID)
if err != nil {
return err
}
*o = *ret
return nil
}
// ReloadAll refetches every row with matching primary key column values
// and overwrites the original object slice with the newly updated slice.
func (o *ScriptExecutionSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
if o == nil || len(*o) == 0 {
return nil
}
slice := ScriptExecutionSlice{}
var args []interface{}
for _, obj := range *o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), scriptExecutionPrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "SELECT \"script_execution\".* FROM \"script_execution\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, scriptExecutionPrimaryKeyColumns, len(*o))
q := queries.Raw(sql, args...)
err := q.Bind(ctx, exec, &slice)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to reload all in ScriptExecutionSlice")
}
*o = slice
return nil
}
// ScriptExecutionExists checks if the ScriptExecution row exists.
func ScriptExecutionExists(ctx context.Context, exec boil.ContextExecutor, iD int64) (bool, error) {
var exists bool
sql := "select exists(select 1 from \"script_execution\" where \"id\"=? limit 1)"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, iD)
}
row := exec.QueryRowContext(ctx, sql, iD)
err := row.Scan(&exists)
if err != nil {
return false, errors.Wrap(err, "sqlite3: unable to check if script_execution exists")
}
return exists, nil
}

View File

@@ -0,0 +1,793 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"bytes"
"context"
"reflect"
"testing"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/randomize"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
var (
// Relationships sometimes use the reflection helper queries.Equal/queries.Assign
// so force a package dependency in case they don't.
_ = queries.Equal
)
func testScriptExecutions(t *testing.T) {
t.Parallel()
query := ScriptExecutions()
if query.Query == nil {
t.Error("expected a query, got nothing")
}
}
func testScriptExecutionsDelete(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := o.Delete(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsQueryDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := ScriptExecutions().DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsSliceDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptExecutionSlice{o}
if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptExecutionsExists(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
e, err := ScriptExecutionExists(ctx, tx, o.ID)
if err != nil {
t.Errorf("Unable to check if ScriptExecution exists: %s", err)
}
if !e {
t.Errorf("Expected ScriptExecutionExists to return true, but got false.")
}
}
func testScriptExecutionsFind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
scriptExecutionFound, err := FindScriptExecution(ctx, tx, o.ID)
if err != nil {
t.Error(err)
}
if scriptExecutionFound == nil {
t.Error("want a record, got nil")
}
}
func testScriptExecutionsBind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = ScriptExecutions().Bind(ctx, tx, o); err != nil {
t.Error(err)
}
}
func testScriptExecutionsOne(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if x, err := ScriptExecutions().One(ctx, tx); err != nil {
t.Error(err)
} else if x == nil {
t.Error("expected to get a non nil record")
}
}
func testScriptExecutionsAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
scriptExecutionOne := &ScriptExecution{}
scriptExecutionTwo := &ScriptExecution{}
if err = randomize.Struct(seed, scriptExecutionOne, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err = randomize.Struct(seed, scriptExecutionTwo, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptExecutionOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptExecutionTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := ScriptExecutions().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 2 {
t.Error("want 2 records, got:", len(slice))
}
}
func testScriptExecutionsCount(t *testing.T) {
t.Parallel()
var err error
seed := randomize.NewSeed()
scriptExecutionOne := &ScriptExecution{}
scriptExecutionTwo := &ScriptExecution{}
if err = randomize.Struct(seed, scriptExecutionOne, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err = randomize.Struct(seed, scriptExecutionTwo, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptExecutionOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptExecutionTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 2 {
t.Error("want 2 records, got:", count)
}
}
func scriptExecutionBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func scriptExecutionAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *ScriptExecution) error {
*o = ScriptExecution{}
return nil
}
func testScriptExecutionsHooks(t *testing.T) {
t.Parallel()
var err error
ctx := context.Background()
empty := &ScriptExecution{}
o := &ScriptExecution{}
seed := randomize.NewSeed()
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, false); err != nil {
t.Errorf("Unable to randomize ScriptExecution object: %s", err)
}
AddScriptExecutionHook(boil.BeforeInsertHook, scriptExecutionBeforeInsertHook)
if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeInsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterInsertHook, scriptExecutionAfterInsertHook)
if err = o.doAfterInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterInsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterSelectHook, scriptExecutionAfterSelectHook)
if err = o.doAfterSelectHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterSelectHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeUpdateHook, scriptExecutionBeforeUpdateHook)
if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeUpdateHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterUpdateHook, scriptExecutionAfterUpdateHook)
if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterUpdateHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeDeleteHook, scriptExecutionBeforeDeleteHook)
if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeDeleteHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterDeleteHook, scriptExecutionAfterDeleteHook)
if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterDeleteHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.BeforeUpsertHook, scriptExecutionBeforeUpsertHook)
if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
}
scriptExecutionBeforeUpsertHooks = []ScriptExecutionHook{}
AddScriptExecutionHook(boil.AfterUpsertHook, scriptExecutionAfterUpsertHook)
if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
}
scriptExecutionAfterUpsertHooks = []ScriptExecutionHook{}
}
func testScriptExecutionsInsert(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptExecutionsInsertWhitelist(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Whitelist(scriptExecutionColumnsWithoutDefault...)); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptExecutionToOneScriptUsingScript(t *testing.T) {
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var local ScriptExecution
var foreign Script
seed := randomize.NewSeed()
if err := randomize.Struct(seed, &local, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if err := randomize.Struct(seed, &foreign, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
local.ScriptID = foreign.ID
if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := local.Script().One(ctx, tx)
if err != nil {
t.Fatal(err)
}
if check.ID != foreign.ID {
t.Errorf("want: %v, got %v", foreign.ID, check.ID)
}
slice := ScriptExecutionSlice{&local}
if err = local.L.LoadScript(ctx, tx, false, (*[]*ScriptExecution)(&slice), nil); err != nil {
t.Fatal(err)
}
if local.R.Script == nil {
t.Error("struct should have been eager loaded")
}
local.R.Script = nil
if err = local.L.LoadScript(ctx, tx, true, &local, nil); err != nil {
t.Fatal(err)
}
if local.R.Script == nil {
t.Error("struct should have been eager loaded")
}
}
func testScriptExecutionToOneSetOpScriptUsingScript(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a ScriptExecution
var b, c Script
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, scriptExecutionDBTypes, false, strmangle.SetComplement(scriptExecutionPrimaryKeyColumns, scriptExecutionColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
for i, x := range []*Script{&b, &c} {
err = a.SetScript(ctx, tx, i != 0, x)
if err != nil {
t.Fatal(err)
}
if a.R.Script != x {
t.Error("relationship struct not set to correct value")
}
if x.R.ScriptExecutions[0] != &a {
t.Error("failed to append to foreign relationship struct")
}
if a.ScriptID != x.ID {
t.Error("foreign key was wrong value", a.ScriptID)
}
zero := reflect.Zero(reflect.TypeOf(a.ScriptID))
reflect.Indirect(reflect.ValueOf(&a.ScriptID)).Set(zero)
if err = a.Reload(ctx, tx); err != nil {
t.Fatal("failed to reload", err)
}
if a.ScriptID != x.ID {
t.Error("foreign key was wrong value", a.ScriptID, x.ID)
}
}
}
func testScriptExecutionsReload(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = o.Reload(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptExecutionsReloadAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptExecutionSlice{o}
if err = slice.ReloadAll(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptExecutionsSelect(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := ScriptExecutions().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 1 {
t.Error("want one record, got:", len(slice))
}
}
var (
scriptExecutionDBTypes = map[string]string{`ID`: `INTEGER`, `ScriptID`: `TEXT`, `ExecutionType`: `TEXT`, `ExecutionStatus`: `TEXT`, `ExecutionTime`: `TIMESTAMP`}
_ = bytes.MinRead
)
func testScriptExecutionsUpdate(t *testing.T) {
t.Parallel()
if 0 == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with no primary key columns")
}
if len(scriptExecutionAllColumns) == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only affect one row but affected", rowsAff)
}
}
func testScriptExecutionsSliceUpdateAll(t *testing.T) {
t.Parallel()
if len(scriptExecutionAllColumns) == len(scriptExecutionPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &ScriptExecution{}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptExecutionDBTypes, true, scriptExecutionPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize ScriptExecution struct: %s", err)
}
// Remove Primary keys and unique columns from what we plan to update
var fields []string
if strmangle.StringSliceMatch(scriptExecutionAllColumns, scriptExecutionPrimaryKeyColumns) {
fields = scriptExecutionAllColumns
} else {
fields = strmangle.SetComplement(
scriptExecutionAllColumns,
scriptExecutionPrimaryKeyColumns,
)
}
value := reflect.Indirect(reflect.ValueOf(o))
typ := reflect.TypeOf(o).Elem()
n := typ.NumField()
updateMap := M{}
for _, col := range fields {
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.Tag.Get("boil") == col {
updateMap[col] = value.Field(i).Interface()
}
}
}
slice := ScriptExecutionSlice{o}
if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("wanted one record updated but got", rowsAff)
}
}

View File

@@ -0,0 +1,838 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"bytes"
"context"
"reflect"
"testing"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/randomize"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
var (
// Relationships sometimes use the reflection helper queries.Equal/queries.Assign
// so force a package dependency in case they don't.
_ = queries.Equal
)
func testScripts(t *testing.T) {
t.Parallel()
query := Scripts()
if query.Query == nil {
t.Error("expected a query, got nothing")
}
}
func testScriptsDelete(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := o.Delete(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptsQueryDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := Scripts().DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptsSliceDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptSlice{o}
if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testScriptsExists(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
e, err := ScriptExists(ctx, tx, o.ID)
if err != nil {
t.Errorf("Unable to check if Script exists: %s", err)
}
if !e {
t.Errorf("Expected ScriptExists to return true, but got false.")
}
}
func testScriptsFind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
scriptFound, err := FindScript(ctx, tx, o.ID)
if err != nil {
t.Error(err)
}
if scriptFound == nil {
t.Error("want a record, got nil")
}
}
func testScriptsBind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = Scripts().Bind(ctx, tx, o); err != nil {
t.Error(err)
}
}
func testScriptsOne(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if x, err := Scripts().One(ctx, tx); err != nil {
t.Error(err)
} else if x == nil {
t.Error("expected to get a non nil record")
}
}
func testScriptsAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
scriptOne := &Script{}
scriptTwo := &Script{}
if err = randomize.Struct(seed, scriptOne, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if err = randomize.Struct(seed, scriptTwo, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Scripts().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 2 {
t.Error("want 2 records, got:", len(slice))
}
}
func testScriptsCount(t *testing.T) {
t.Parallel()
var err error
seed := randomize.NewSeed()
scriptOne := &Script{}
scriptTwo := &Script{}
if err = randomize.Struct(seed, scriptOne, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if err = randomize.Struct(seed, scriptTwo, scriptDBTypes, false, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = scriptOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = scriptTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 2 {
t.Error("want 2 records, got:", count)
}
}
func scriptBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func scriptAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Script) error {
*o = Script{}
return nil
}
func testScriptsHooks(t *testing.T) {
t.Parallel()
var err error
ctx := context.Background()
empty := &Script{}
o := &Script{}
seed := randomize.NewSeed()
if err = randomize.Struct(seed, o, scriptDBTypes, false); err != nil {
t.Errorf("Unable to randomize Script object: %s", err)
}
AddScriptHook(boil.BeforeInsertHook, scriptBeforeInsertHook)
if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
}
scriptBeforeInsertHooks = []ScriptHook{}
AddScriptHook(boil.AfterInsertHook, scriptAfterInsertHook)
if err = o.doAfterInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
}
scriptAfterInsertHooks = []ScriptHook{}
AddScriptHook(boil.AfterSelectHook, scriptAfterSelectHook)
if err = o.doAfterSelectHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
}
scriptAfterSelectHooks = []ScriptHook{}
AddScriptHook(boil.BeforeUpdateHook, scriptBeforeUpdateHook)
if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
}
scriptBeforeUpdateHooks = []ScriptHook{}
AddScriptHook(boil.AfterUpdateHook, scriptAfterUpdateHook)
if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
}
scriptAfterUpdateHooks = []ScriptHook{}
AddScriptHook(boil.BeforeDeleteHook, scriptBeforeDeleteHook)
if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
}
scriptBeforeDeleteHooks = []ScriptHook{}
AddScriptHook(boil.AfterDeleteHook, scriptAfterDeleteHook)
if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
}
scriptAfterDeleteHooks = []ScriptHook{}
AddScriptHook(boil.BeforeUpsertHook, scriptBeforeUpsertHook)
if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
}
scriptBeforeUpsertHooks = []ScriptHook{}
AddScriptHook(boil.AfterUpsertHook, scriptAfterUpsertHook)
if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
}
scriptAfterUpsertHooks = []ScriptHook{}
}
func testScriptsInsert(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptsInsertWhitelist(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Whitelist(scriptColumnsWithoutDefault...)); err != nil {
t.Error(err)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testScriptToManyScriptExecutions(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Script
var b, c ScriptExecution
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, scriptExecutionDBTypes, false, scriptExecutionColumnsWithDefault...); err != nil {
t.Fatal(err)
}
b.ScriptID = a.ID
c.ScriptID = a.ID
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := a.ScriptExecutions().All(ctx, tx)
if err != nil {
t.Fatal(err)
}
bFound, cFound := false, false
for _, v := range check {
if v.ScriptID == b.ScriptID {
bFound = true
}
if v.ScriptID == c.ScriptID {
cFound = true
}
}
if !bFound {
t.Error("expected to find b")
}
if !cFound {
t.Error("expected to find c")
}
slice := ScriptSlice{&a}
if err = a.L.LoadScriptExecutions(ctx, tx, false, (*[]*Script)(&slice), nil); err != nil {
t.Fatal(err)
}
if got := len(a.R.ScriptExecutions); got != 2 {
t.Error("number of eager loaded records wrong, got:", got)
}
a.R.ScriptExecutions = nil
if err = a.L.LoadScriptExecutions(ctx, tx, true, &a, nil); err != nil {
t.Fatal(err)
}
if got := len(a.R.ScriptExecutions); got != 2 {
t.Error("number of eager loaded records wrong, got:", got)
}
if t.Failed() {
t.Logf("%#v", check)
}
}
func testScriptToManyAddOpScriptExecutions(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Script
var b, c, d, e ScriptExecution
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, scriptDBTypes, false, strmangle.SetComplement(scriptPrimaryKeyColumns, scriptColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
foreigners := []*ScriptExecution{&b, &c, &d, &e}
for _, x := range foreigners {
if err = randomize.Struct(seed, x, scriptExecutionDBTypes, false, strmangle.SetComplement(scriptExecutionPrimaryKeyColumns, scriptExecutionColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
foreignersSplitByInsertion := [][]*ScriptExecution{
{&b, &c},
{&d, &e},
}
for i, x := range foreignersSplitByInsertion {
err = a.AddScriptExecutions(ctx, tx, i != 0, x...)
if err != nil {
t.Fatal(err)
}
first := x[0]
second := x[1]
if a.ID != first.ScriptID {
t.Error("foreign key was wrong value", a.ID, first.ScriptID)
}
if a.ID != second.ScriptID {
t.Error("foreign key was wrong value", a.ID, second.ScriptID)
}
if first.R.Script != &a {
t.Error("relationship was not added properly to the foreign slice")
}
if second.R.Script != &a {
t.Error("relationship was not added properly to the foreign slice")
}
if a.R.ScriptExecutions[i*2] != first {
t.Error("relationship struct slice not set to correct value")
}
if a.R.ScriptExecutions[i*2+1] != second {
t.Error("relationship struct slice not set to correct value")
}
count, err := a.ScriptExecutions().Count(ctx, tx)
if err != nil {
t.Fatal(err)
}
if want := int64((i + 1) * 2); count != want {
t.Error("want", want, "got", count)
}
}
}
func testScriptsReload(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = o.Reload(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptsReloadAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := ScriptSlice{o}
if err = slice.ReloadAll(ctx, tx); err != nil {
t.Error(err)
}
}
func testScriptsSelect(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Scripts().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 1 {
t.Error("want one record, got:", len(slice))
}
}
var (
scriptDBTypes = map[string]string{`ID`: `TEXT`, `ScriptID`: `TEXT`, `ScriptName`: `TEXT`, `ScriptPath`: `TEXT`, `ScriptData`: `BLOB`, `LastExecutedAt`: `TIMESTAMP`, `CreatedAt`: `TIMESTAMP`}
_ = bytes.MinRead
)
func testScriptsUpdate(t *testing.T) {
t.Parallel()
if 0 == len(scriptPrimaryKeyColumns) {
t.Skip("Skipping table with no primary key columns")
}
if len(scriptAllColumns) == len(scriptPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only affect one row but affected", rowsAff)
}
}
func testScriptsSliceUpdateAll(t *testing.T) {
t.Parallel()
if len(scriptAllColumns) == len(scriptPrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Script{}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Scripts().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, scriptDBTypes, true, scriptPrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Script struct: %s", err)
}
// Remove Primary keys and unique columns from what we plan to update
var fields []string
if strmangle.StringSliceMatch(scriptAllColumns, scriptPrimaryKeyColumns) {
fields = scriptAllColumns
} else {
fields = strmangle.SetComplement(
scriptAllColumns,
scriptPrimaryKeyColumns,
)
}
value := reflect.Indirect(reflect.ValueOf(o))
typ := reflect.TypeOf(o).Elem()
n := typ.NumField()
updateMap := M{}
for _, col := range fields {
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.Tag.Get("boil") == col {
updateMap[col] = value.Field(i).Interface()
}
}
}
slice := ScriptSlice{o}
if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("wanted one record updated but got", rowsAff)
}
}

View File

@@ -0,0 +1,126 @@
package script
import (
"context"
"time"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/database"
modelPSQL "github.com/thrasher-corp/gocryptotrader/database/models/postgres"
modelSQLite "github.com/thrasher-corp/gocryptotrader/database/models/sqlite3"
"github.com/thrasher-corp/gocryptotrader/database/repository"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/volatiletech/null"
)
// Event inserts a new script event into database with execution details (script name time status hash of script)
func Event(id, name, path string, data null.Bytes, executionType, status string, time time.Time) {
if database.DB.SQL == nil {
return
}
ctx := context.Background()
ctx = boil.SkipTimestamps(ctx)
tx, err := database.DB.SQL.BeginTx(ctx, nil)
if err != nil {
log.Errorf(log.DatabaseMgr, "Event transaction begin failed: %v", err)
return
}
if repository.GetSQLDialect() == database.DBSQLite3 {
query := modelSQLite.ScriptWhere.ScriptID.EQ(id)
f, errQry := modelSQLite.Scripts(query).Exists(ctx, tx)
if errQry != nil {
log.Errorf(log.DatabaseMgr, "Query failed: %v", errQry)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
return
}
var tempEvent = modelSQLite.Script{}
if !f {
newUUID, errUUID := uuid.NewV4()
if errUUID != nil {
log.Errorf(log.DatabaseMgr, "Failed to generate UUID: %v", errUUID)
_ = tx.Rollback()
return
}
tempEvent.ID = newUUID.String()
tempEvent.ScriptID = id
tempEvent.ScriptName = name
tempEvent.ScriptPath = path
tempEvent.ScriptData = data
err = tempEvent.Insert(ctx, tx, boil.Infer())
if err != nil {
log.Errorf(log.DatabaseMgr, "Event insert failed: %v", err)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
return
}
} else {
tempEvent.ID = id
}
tempScriptExecution := &modelSQLite.ScriptExecution{
ScriptID: id,
ExecutionTime: time.UTC().String(),
ExecutionStatus: status,
ExecutionType: executionType,
}
err = tempEvent.AddScriptExecutions(ctx, tx, true, tempScriptExecution)
if err != nil {
log.Errorf(log.DatabaseMgr, "Event insert failed: %v", err)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
return
}
} else {
var tempEvent = modelPSQL.Script{
ScriptID: id,
ScriptName: name,
ScriptPath: path,
ScriptData: data,
}
err = tempEvent.Upsert(ctx, tx, true, []string{"script_id"}, boil.Whitelist("last_executed_at"), boil.Infer())
if err != nil {
log.Errorf(log.DatabaseMgr, "Event insert failed: %v", err)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
return
}
tempScriptExecution := &modelPSQL.ScriptExecution{
ExecutionTime: time.UTC(),
ExecutionStatus: status,
ExecutionType: executionType,
}
err = tempEvent.AddScriptExecutions(ctx, tx, true, tempScriptExecution)
if err != nil {
log.Errorf(log.DatabaseMgr, "Event insert failed: %v", err)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
return
}
}
err = tx.Commit()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction commit failed: %v", err)
err = tx.Rollback()
if err != nil {
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
}
}
}

View File

@@ -0,0 +1,92 @@
package tests
import (
"fmt"
"path/filepath"
"sync"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/database"
"github.com/thrasher-corp/gocryptotrader/database/drivers"
"github.com/thrasher-corp/gocryptotrader/database/repository"
"github.com/thrasher-corp/gocryptotrader/database/repository/script"
"github.com/thrasher-corp/goose"
"github.com/volatiletech/null"
)
func TestScript(t *testing.T) {
testCases := []struct {
name string
config *database.Config
runner func()
closer func(t *testing.T, dbConn *database.Db) error
output interface{}
}{
{
"SQLite-Write",
&database.Config{
Driver: database.DBSQLite3,
ConnectionDetails: drivers.ConnectionDetails{Database: "./testdb"},
},
writeScript,
closeDatabase,
nil,
},
{
"Postgres-Write",
postgresTestDatabase,
writeScript,
nil,
nil,
},
}
for _, tests := range testCases {
test := tests
t.Run(test.name, func(t *testing.T) {
if !checkValidConfig(t, &test.config.ConnectionDetails) {
t.Skip("database not configured skipping test")
}
dbConn, err := connectToDatabase(t, test.config)
if err != nil {
t.Fatal(err)
}
path := filepath.Join("..", "migrations")
err = goose.Run("up", dbConn.SQL, repository.GetSQLDialect(), path, "")
if err != nil {
t.Fatalf("failed to run migrations %v", err)
}
if test.runner != nil {
test.runner()
}
if test.closer != nil {
err = test.closer(t, dbConn)
if err != nil {
t.Log(err)
}
}
})
}
}
func writeScript() {
var wg sync.WaitGroup
for x := 0; x < 20; x++ {
wg.Add(1)
go func(x int) {
defer wg.Done()
test := fmt.Sprintf("test-%v", x)
var data null.Bytes
script.Event(test, test, test, data, test, test, time.Now())
}(x)
}
wg.Wait()
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/dispatch"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/portfolio"
"github.com/thrasher-corp/gocryptotrader/utils"
@@ -32,6 +33,7 @@ type Engine struct {
NTPManager ntpManager
ConnectionManager connectionManager
DatabaseManager databaseManager
GctScriptManager gctScriptManager
OrderManager orderManager
PortfolioManager portfolioManager
CommsManager commsManager
@@ -115,6 +117,8 @@ func ValidateSettings(b *Engine, s *Settings) {
b.Settings.EnablePortfolioManager = s.EnablePortfolioManager
b.Settings.EnableCoinmarketcapAnalysis = s.EnableCoinmarketcapAnalysis
b.Settings.EnableDatabaseManager = s.EnableDatabaseManager
b.Settings.EnableGCTScriptManager = s.EnableGCTScriptManager
b.Settings.MaxVirtualMachines = s.MaxVirtualMachines
b.Settings.EnableDispatcher = s.EnableDispatcher
if flagSet["grpc"] {
@@ -141,6 +145,14 @@ func ValidateSettings(b *Engine, s *Settings) {
b.Settings.EnableDeprecatedRPC = b.Config.RemoteControl.DeprecatedRPC.Enabled
}
if flagSet["gctscriptmanager"] {
gctscript.GCTScriptConfig.Enabled = s.EnableGCTScriptManager
}
if flagSet["maxvirtualmachines"] {
gctscript.GCTScriptConfig.MaxVirtualMachines = uint8(s.MaxVirtualMachines)
}
b.Settings.EnableCommsRelayer = s.EnableCommsRelayer
b.Settings.EnableEventManager = s.EnableEventManager
@@ -266,10 +278,14 @@ func PrintSettings(s *Settings) {
log.Debugf(log.Global, "\t Exchange HTTP timeout: %v", s.ExchangeHTTPTimeout)
log.Debugf(log.Global, "\t Exchange HTTP user agent: %v", s.ExchangeHTTPUserAgent)
log.Debugf(log.Global, "\t Exchange HTTP proxy: %v\n", s.ExchangeHTTPProxy)
log.Debugf(log.Global, "- GCTSCRIPT SETTINGS: ")
log.Debugf(log.Global, "\t Enable GCTScript manager: %v", s.EnableGCTScriptManager)
log.Debugf(log.Global, "\t GCTScript max virtual machines: %v", s.MaxVirtualMachines)
log.Debugf(log.Global, "- COMMON SETTINGS:")
log.Debugf(log.Global, "\t Global HTTP timeout: %v", s.GlobalHTTPTimeout)
log.Debugf(log.Global, "\t Global HTTP user agent: %v", s.GlobalHTTPUserAgent)
log.Debugf(log.Global, "\t Global HTTP proxy: %v", s.ExchangeHTTPProxy)
log.Debugln(log.Global)
}
@@ -423,6 +439,14 @@ func (e *Engine) Start() error {
go WebsocketRoutine()
}
if e.Settings.EnableGCTScriptManager {
if e.Config.GCTScript.Enabled {
if err := e.GctScriptManager.Start(); err != nil {
log.Errorf(log.Global, "GCTScript manager unable to start: %v", err)
}
}
}
return nil
}
@@ -434,6 +458,11 @@ func (e *Engine) Stop() {
e.Config.Portfolio = portfolio.Portfolio
}
if e.GctScriptManager.Started() {
if err := e.GctScriptManager.Stop(); err != nil {
log.Errorf(log.Global, "GCTScript manager unable to stop. Error: %v", err)
}
}
if e.OrderManager.Started() {
if err := e.OrderManager.Stop(); err != nil {
log.Errorf(log.Global, "Order manager unable to stop. Error: %v", err)

View File

@@ -28,6 +28,7 @@ type Settings struct {
EnableOrderManager bool
EnableConnectivityMonitor bool
EnableDatabaseManager bool
EnableGCTScriptManager bool
EnableNTPClient bool
EnableWebsocketRoutine bool
EventManagerDelay time.Duration
@@ -73,4 +74,35 @@ type Settings struct {
EnableDispatcher bool
DispatchMaxWorkerAmount int
DispatchJobsLimit int
// GCTscript settings
MaxVirtualMachines uint
}
const (
// ErrSubSystemAlreadyStarted message to return when a subsystem is already started
ErrSubSystemAlreadyStarted = "manager already started"
// ErrSubSystemAlreadyStopped message to return when a subsystem is already stopped
ErrSubSystemAlreadyStopped = "already stopped"
// ErrSubSystemNotStarted message to return when subsystem not started
ErrSubSystemNotStarted = "not started"
// ErrScriptFailedValidation message to display when a script fails its validation
ErrScriptFailedValidation string = "validation failed"
// MsgSubSystemStarting message to return when subsystem is starting up
MsgSubSystemStarting = "manager starting..."
// MsgSubSystemStarted message to return when subsystem has started
MsgSubSystemStarted = "started."
// MsgSubSystemShuttingDown message to return when a subsystem is shutting down
MsgSubSystemShuttingDown = "shutting down..."
// MsgSubSystemShutdown message to return when a subsystem has shutdown
MsgSubSystemShutdown = "manager shutdown."
// MsgStatusOK message to display when status is "OK"
MsgStatusOK string = "ok"
// MsgStatusSuccess message to display when status is successful
MsgStatusSuccess string = "success"
// MsgStatusError message to display when failure occurs
MsgStatusError string = "error"
)

View File

@@ -308,8 +308,9 @@ func TestIsValidExchange(t *testing.T) {
if s := IsValidExchange("invalidexchangerino"); s {
t.Error("unexpected result")
}
loadConfig(t)
if !configLoaded {
loadConfig(t)
}
if s := IsValidExchange(testExchange); !s {
t.Error("unexpected result")
}

98
engine/gctscript.go Normal file
View File

@@ -0,0 +1,98 @@
package engine
import (
"fmt"
"path/filepath"
"sync/atomic"
"github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
const gctscriptManagerName = "GCTScript"
type gctScriptManager struct {
started int32
stopped int32
shutdown chan struct{}
}
// Started returns if gctscript manager subsystem is started
func (g *gctScriptManager) Started() bool {
return atomic.LoadInt32(&g.started) == 1
}
// Start starts gctscript subsystem and creates shutdown channel
func (g *gctScriptManager) Start() (err error) {
if atomic.AddInt32(&g.started, 1) != 1 {
return fmt.Errorf("%s %s", gctscriptManagerName, ErrSubSystemAlreadyStarted)
}
defer func() {
if err != nil {
atomic.CompareAndSwapInt32(&g.started, 1, 0)
}
}()
log.Debugln(log.Global, gctscriptManagerName, MsgSubSystemStarting)
g.shutdown = make(chan struct{})
go g.run()
return nil
}
// Stop stops gctscript subsystem along with all running Virtual Machines
func (g *gctScriptManager) Stop() error {
if atomic.LoadInt32(&g.started) == 0 {
return fmt.Errorf("%s %s", gctscriptManagerName, ErrSubSystemNotStarted)
}
if atomic.AddInt32(&g.stopped, 1) != 1 {
return fmt.Errorf("%s %s", gctscriptManagerName, ErrSubSystemAlreadyStopped)
}
log.Debugln(log.GCTScriptMgr, gctscriptManagerName, MsgSubSystemShuttingDown)
close(g.shutdown)
err := vm.ShutdownAll()
if err != nil {
return err
}
return nil
}
func (g *gctScriptManager) run() {
log.Debugln(log.Global, gctscriptManagerName, MsgSubSystemStarted)
Bot.ServicesWG.Add(1)
g.autoLoad()
defer func() {
atomic.CompareAndSwapInt32(&g.stopped, 1, 0)
atomic.CompareAndSwapInt32(&g.started, 1, 0)
Bot.ServicesWG.Done()
log.Debugln(log.GCTScriptMgr, gctscriptManagerName, MsgSubSystemShutdown)
}()
<-g.shutdown
}
func (g *gctScriptManager) autoLoad() {
for x := range Bot.Config.GCTScript.AutoLoad {
temp := vm.New()
if temp == nil {
log.Errorf(log.GCTScriptMgr, "Unable to create Virtual Machine, autoload failed for: %v",
Bot.Config.GCTScript.AutoLoad[x])
continue
}
var name = Bot.Config.GCTScript.AutoLoad[x]
if filepath.Ext(name) != ".gct" {
name += ".gct"
}
scriptPath := filepath.Join(vm.ScriptPath, name)
err := temp.Load(scriptPath)
if err != nil {
log.Errorf(log.GCTScriptMgr, "%v failed to load: %v", filepath.Base(scriptPath), err)
continue
}
go temp.CompileAndRun()
}
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/stats"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
"github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/portfolio"
"github.com/thrasher-corp/gocryptotrader/utils"
@@ -44,6 +45,7 @@ func GetSubsystemsStatus() map[string]bool {
systems["exchange_syncer"] = Bot.Settings.EnableExchangeSyncManager
systems["grpc"] = Bot.Settings.EnableGRPC
systems["grpc_proxy"] = Bot.Settings.EnableGRPCProxy
systems["gctscript"] = Bot.GctScriptManager.Started()
systems["deprecated_rpc"] = Bot.Settings.EnableDeprecatedRPC
systems["websocket_rpc"] = Bot.Settings.EnableWebsocketRPC
systems["dispatch"] = dispatch.IsRunning()
@@ -121,7 +123,15 @@ func SetSubsystem(subsys string, enable bool) error {
return dispatch.Start(Bot.Settings.DispatchMaxWorkerAmount, Bot.Settings.DispatchJobsLimit)
}
return dispatch.Stop()
case "gctscript":
if enable {
vm.GCTScriptConfig.Enabled = true
return Bot.GctScriptManager.Start()
}
vm.GCTScriptConfig.Enabled = false
return Bot.GctScriptManager.Stop()
}
return errors.New("subsystem not found")
}
@@ -417,19 +427,21 @@ func GetRelatableCurrencies(p currency.Pair, incOrig, incUSDT bool) currency.Pai
// GetSpecificOrderbook returns a specific orderbook given the currency,
// exchangeName and assetType
func GetSpecificOrderbook(p currency.Pair, exchangeName string, assetType asset.Item) (*orderbook.Base, error) {
if CheckExchangeExists(exchangeName) {
return GetExchangeByName(exchangeName).FetchOrderbook(p, assetType)
exch := GetExchangeByName(exchangeName)
if exch == nil {
return nil, ErrExchangeNotFound
}
return &orderbook.Base{}, errors.New("exchange is not loaded/doesn't exist")
return exch.FetchOrderbook(p, assetType)
}
// GetSpecificTicker returns a specific ticker given the currency,
// exchangeName and assetType
func GetSpecificTicker(p currency.Pair, exchangeName string, assetType asset.Item) (*ticker.Price, error) {
if CheckExchangeExists(exchangeName) {
return GetExchangeByName(exchangeName).FetchTicker(p, assetType)
exch := GetExchangeByName(exchangeName)
if exch == nil {
return nil, ErrExchangeNotFound
}
return &ticker.Price{}, errors.New("exchange is not loaded/doesn't exist")
return exch.FetchTicker(p, assetType)
}
// GetCollatedExchangeAccountInfoByCoin collates individual exchange account

View File

@@ -70,6 +70,7 @@ func (o *orderManager) Start() error {
go o.run()
return nil
}
func (o *orderManager) Stop() error {
if atomic.LoadInt32(&o.started) == 0 {
return errors.New("order manager not started")

View File

@@ -17,6 +17,7 @@ func loadConfig(t *testing.T) *config.Config {
if err != nil {
t.Error("GetCurrencyConfig LoadConfig error", err)
}
configLoaded = true
return cfg
}

View File

@@ -4,16 +4,22 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/gofrs/uuid"
grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
grpcruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/common/file"
"github.com/thrasher-corp/gocryptotrader/common/file/archive"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/database/models/postgres"
"github.com/thrasher-corp/gocryptotrader/database/models/sqlite3"
@@ -24,6 +30,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/gctrpc"
"github.com/thrasher-corp/gocryptotrader/gctrpc/auth"
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/portfolio"
"github.com/thrasher-corp/gocryptotrader/utils"
@@ -1213,3 +1220,296 @@ func (s *RPCServer) GetAuditEvent(ctx context.Context, r *gctrpc.GetAuditEventRe
return &resp, nil
}
// GCTScriptStatus returns a slice of current running scripts that includes next run time and uuid
func (s *RPCServer) GCTScriptStatus(ctx context.Context, r *gctrpc.GCTScriptStatusRequest) (*gctrpc.GCTScriptStatusResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptStatusResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
if gctscript.VMSCount.Len() < 1 {
return &gctrpc.GCTScriptStatusResponse{Status: "no scripts running"}, nil
}
resp := &gctrpc.GCTScriptStatusResponse{
Status: fmt.Sprintf("%v of %v virtual machines running", gctscript.VMSCount.Len(), gctscript.GCTScriptConfig.MaxVirtualMachines),
}
gctscript.AllVMSync.Range(func(k, v interface{}) bool {
vm := v.(*gctscript.VM)
resp.Scripts = append(resp.Scripts, &gctrpc.GCTScript{
UUID: vm.ID.String(),
Name: vm.ShortName(),
NextRun: vm.NextRun.String(),
})
return true
})
return resp, nil
}
// GCTScriptQuery queries a running script and returns script running information
func (s *RPCServer) GCTScriptQuery(ctx context.Context, r *gctrpc.GCTScriptQueryRequest) (*gctrpc.GCTScriptQueryResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptQueryResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
UUID, err := uuid.FromString(r.Script.UUID)
if err != nil {
return &gctrpc.GCTScriptQueryResponse{Status: MsgStatusError, Data: err.Error()}, nil
}
if v, f := gctscript.AllVMSync.Load(UUID); f {
resp := &gctrpc.GCTScriptQueryResponse{
Status: MsgStatusOK,
Script: &gctrpc.GCTScript{
Name: v.(*gctscript.VM).ShortName(),
UUID: v.(*gctscript.VM).ID.String(),
Path: v.(*gctscript.VM).Path,
NextRun: v.(*gctscript.VM).NextRun.String(),
},
}
data, err := v.(*gctscript.VM).Read()
if err != nil {
return nil, err
}
resp.Data = string(data)
return resp, nil
}
return &gctrpc.GCTScriptQueryResponse{Status: MsgStatusError, Data: "UUID not found"}, nil
}
// GCTScriptExecute execute a script
func (s *RPCServer) GCTScriptExecute(ctx context.Context, r *gctrpc.GCTScriptExecuteRequest) (*gctrpc.GCTScriptGenericResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
if r.Script.Path == "" {
r.Script.Path = gctscript.ScriptPath
}
gctVM := gctscript.New()
if gctVM == nil {
return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: "unable to create VM instance"}, nil
}
script := filepath.Join(r.Script.Path, r.Script.Name)
err := gctVM.Load(script)
if err != nil {
return &gctrpc.GCTScriptGenericResponse{
Status: MsgStatusError,
Data: err.Error(),
}, nil
}
go gctVM.CompileAndRun()
return &gctrpc.GCTScriptGenericResponse{
Status: MsgStatusOK,
Data: gctVM.ShortName() + " (" + gctVM.ID.String() + ") executed",
}, nil
}
// GCTScriptStop terminate a running script
func (s *RPCServer) GCTScriptStop(ctx context.Context, r *gctrpc.GCTScriptStopRequest) (*gctrpc.GCTScriptGenericResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
UUID, err := uuid.FromString(r.Script.UUID)
if err != nil {
return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: err.Error()}, nil
}
if v, f := gctscript.AllVMSync.Load(UUID); f {
err = v.(*gctscript.VM).Shutdown()
status := " terminated"
if err != nil {
status = " " + err.Error()
}
return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusOK, Data: v.(*gctscript.VM).ID.String() + status}, nil
}
return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: "no running script found"}, nil
}
// GCTScriptUpload upload a new script to ScriptPath
func (s *RPCServer) GCTScriptUpload(ctx context.Context, r *gctrpc.GCTScriptUploadRequest) (*gctrpc.GCTScriptGenericResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
fPath := filepath.Join(gctscript.ScriptPath, r.ScriptName)
var fPathExits = fPath
if filepath.Ext(fPath) == ".zip" {
fPathExits = fPathExits[0 : len(fPathExits)-4]
}
if s, err := os.Stat(fPathExits); !os.IsNotExist(err) {
if !r.Overwrite {
return nil, fmt.Errorf("%s script found and overwrite set to false", r.ScriptName)
}
f := filepath.Join(gctscript.ScriptPath, "version_history")
err = os.MkdirAll(f, 0770)
if err != nil {
return nil, err
}
timeString := strconv.FormatInt(time.Now().UnixNano(), 10)
renamedFile := filepath.Join(f, timeString+"-"+filepath.Base(fPathExits))
if s.IsDir() {
err = archive.Zip(fPathExits, renamedFile+".zip")
if err != nil {
return nil, err
}
} else {
err = file.Move(fPathExits, renamedFile)
if err != nil {
return nil, err
}
}
}
newFile, err := os.Create(fPath)
if err != nil {
return nil, err
}
_, err = newFile.Write(r.Data)
if err != nil {
return nil, err
}
err = newFile.Close()
if err != nil {
log.Errorln(log.Global, "Failed to close file handle, archive removal may fail")
}
if r.Archived {
files, errExtract := archive.UnZip(fPath, filepath.Join(gctscript.ScriptPath, r.ScriptName[:len(r.ScriptName)-4]))
if errExtract != nil {
log.Errorf(log.Global, "Failed to archive zip file %v", errExtract)
return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: errExtract.Error()}, nil
}
var failedFiles []string
for x := range files {
err = gctscript.Validate(files[x])
if err != nil {
failedFiles = append(failedFiles, files[x])
}
}
err = os.Remove(fPath)
if err != nil {
return nil, err
}
if len(failedFiles) > 0 {
err = os.RemoveAll(filepath.Join(gctscript.ScriptPath, r.ScriptName[:len(r.ScriptName)-4]))
if err != nil {
log.Errorf(log.GCTScriptMgr, "Failed to remove file %v (%v), manual deletion required", filepath.Base(fPath), err)
}
return &gctrpc.GCTScriptGenericResponse{Status: ErrScriptFailedValidation, Data: strings.Join(failedFiles, ", ")}, nil
}
} else {
err = gctscript.Validate(fPath)
if err != nil {
errRemove := os.Remove(fPath)
if errRemove != nil {
log.Errorf(log.GCTScriptMgr, "Failed to remove file %v, manual deletion required: %v", filepath.Base(fPath), errRemove)
}
return &gctrpc.GCTScriptGenericResponse{Status: ErrScriptFailedValidation, Data: err.Error()}, nil
}
}
return &gctrpc.GCTScriptGenericResponse{
Status: MsgStatusOK,
Data: fmt.Sprintf("script %s written", newFile.Name()),
}, nil
}
// GCTScriptReadScript read a script and return contents
func (s *RPCServer) GCTScriptReadScript(ctx context.Context, r *gctrpc.GCTScriptReadScriptRequest) (*gctrpc.GCTScriptQueryResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptQueryResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
filename := filepath.Join(gctscript.ScriptPath, r.Script.Name)
if !strings.HasPrefix(filename, filepath.Clean(gctscript.ScriptPath)+string(os.PathSeparator)) {
return nil, fmt.Errorf("%s: invalid file path", filename)
}
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &gctrpc.GCTScriptQueryResponse{
Status: MsgStatusOK,
Script: &gctrpc.GCTScript{
Name: filepath.Base(filename),
Path: filepath.Dir(filename),
},
Data: string(data),
}, nil
}
// GCTScriptListAll lists all scripts inside the default script path
func (s *RPCServer) GCTScriptListAll(context.Context, *gctrpc.GCTScriptListAllRequest) (*gctrpc.GCTScriptStatusResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptStatusResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
resp := &gctrpc.GCTScriptStatusResponse{}
err := filepath.Walk(gctscript.ScriptPath,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) == ".gct" {
resp.Scripts = append(resp.Scripts, &gctrpc.GCTScript{
Name: path,
})
}
return nil
})
if err != nil {
return nil, err
}
return resp, nil
}
// GCTScriptStopAll stops all running scripts
func (s *RPCServer) GCTScriptStopAll(context.Context, *gctrpc.GCTScriptStopAllRequest) (*gctrpc.GCTScriptGenericResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
err := gctscript.ShutdownAll()
if err != nil {
return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil
}
return &gctrpc.GCTScriptGenericResponse{
Status: MsgStatusOK,
Data: "all running scripts have been stopped",
}, nil
}
// GCTScriptAutoLoadToggle adds or removes an entry to the autoload list
func (s *RPCServer) GCTScriptAutoLoadToggle(ctx context.Context, r *gctrpc.GCTScriptAutoLoadRequest) (*gctrpc.GCTScriptGenericResponse, error) {
if !gctscript.GCTScriptConfig.Enabled {
return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil
}
if r.Status {
err := gctscript.Autoload(r.Script, true)
if err != nil {
return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil
}
return &gctrpc.GCTScriptGenericResponse{Status: "success", Data: "script " + r.Script + " removed from autoload list"}, nil
}
err := gctscript.Autoload(r.Script, false)
if err != nil {
return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil
}
return &gctrpc.GCTScriptGenericResponse{Status: "success", Data: "script " + r.Script + " added to autoload list"}, nil
}

View File

@@ -0,0 +1,74 @@
package withdraw
import (
"errors"
"strings"
"github.com/thrasher-corp/gocryptotrader/currency"
)
// Valid takes interface and passes to asset type to check the request meets requirements to submit
func Valid(request interface{}) error {
switch request := request.(type) {
case *FiatRequest:
return ValidateFiat(request)
case *CryptoRequest:
return ValidateCrypto(request)
default:
return ErrInvalidRequest
}
}
// ValidateFiat checks if Fiat request is valid
func ValidateFiat(request *FiatRequest) (err error) {
if request == nil {
return ErrRequestCannotBeNil
}
var allErrors []string
if (request.Currency != currency.Code{}) {
if !request.Currency.IsFiatCurrency() {
allErrors = append(allErrors, "currency is not a fiat currency")
}
} else {
allErrors = append(allErrors, ErrStrNoCurrencySet)
}
if request.Amount <= 0 {
allErrors = append(allErrors, ErrStrAmountMustBeGreaterThanZero)
}
if len(allErrors) > 0 {
err = errors.New(strings.Join(allErrors, ", "))
}
return err
}
// ValidateCrypto checks if Crypto request is valid
func ValidateCrypto(request *CryptoRequest) (err error) {
if request == nil {
return ErrRequestCannotBeNil
}
var allErrors []string
if (request.Currency != currency.Code{}) {
if !request.Currency.IsCryptocurrency() {
allErrors = append(allErrors, "currency is not a crypto currency")
}
} else {
allErrors = append(allErrors, ErrStrNoCurrencySet)
}
if request.Amount <= 0 {
allErrors = append(allErrors, ErrStrAmountMustBeGreaterThanZero)
}
if request.Address == "" {
allErrors = append(allErrors, ErrStrAddressNotSet)
}
if len(allErrors) > 0 {
err = errors.New(strings.Join(allErrors, ", "))
}
return err
}

View File

@@ -0,0 +1,178 @@
package withdraw
import (
"errors"
"testing"
"github.com/thrasher-corp/gocryptotrader/currency"
)
var (
validFiatRequest = &FiatRequest{
GenericInfo: GenericInfo{
Currency: currency.AUD,
Description: "Test Withdrawal",
Amount: 0.1,
},
BankAccountName: "test-bank-account",
BankAccountNumber: "test-bank-number",
BankName: "test-bank-name",
BSB: "",
SwiftCode: "",
IBAN: "",
}
invalidFiatRequest = &FiatRequest{}
invalidCurrencyFiatRequest = &FiatRequest{
GenericInfo: GenericInfo{
Currency: currency.BTC,
Amount: 1,
},
}
validCryptoRequest = &CryptoRequest{
GenericInfo: GenericInfo{
Currency: currency.BTC,
Description: "Test Withdrawal",
Amount: 0.1,
},
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
invalidCryptoRequest = &CryptoRequest{}
invalidCurrencyCryptoRequest = &CryptoRequest{
GenericInfo: GenericInfo{
Currency: currency.AUD,
Amount: 0,
},
}
invalidCryptoAddressRequest = &CryptoRequest{
GenericInfo: GenericInfo{
Currency: currency.BTC,
Description: "Test Withdrawal",
Amount: 0.1,
},
Address: "1D10TH0RS3",
}
)
func TestValid(t *testing.T) {
testCases := []struct {
name string
request interface{}
output interface{}
}{
{
"Fiat",
validFiatRequest,
nil,
},
{
"Crypto",
validCryptoRequest,
nil,
},
{
"Invalid",
nil,
ErrInvalidRequest,
},
}
for _, tests := range testCases {
test := tests
t.Run(test.name, func(t *testing.T) {
err := Valid(test.request)
if err != nil {
if test.output.(error).Error() != err.Error() {
t.Fatal(err)
}
}
})
}
}
func TestValidateFiat(t *testing.T) {
testCases := []struct {
name string
request *FiatRequest
output interface{}
}{
{
"Valid",
validFiatRequest,
nil,
},
{
"Invalid",
invalidFiatRequest,
errors.New("currency not set, amount must be greater than 0"),
},
{
"NoRequest",
nil,
ErrRequestCannotBeNil,
},
{
"CryptoCurrency",
invalidCurrencyFiatRequest,
errors.New("currency is not a fiat currency"),
},
}
for _, tests := range testCases {
test := tests
t.Run(test.name, func(t *testing.T) {
err := ValidateFiat(test.request)
if err != nil {
if test.output.(error).Error() != err.Error() {
t.Fatal(err)
}
}
})
}
}
func TestValidateCrypto(t *testing.T) {
testCases := []struct {
name string
request *CryptoRequest
output interface{}
}{
{
"Valid",
validCryptoRequest,
nil,
},
{
"Invalid",
invalidCryptoRequest,
errors.New("currency not set, amount must be greater than 0, address cannot be empty"),
},
{
"NoRequest",
nil,
ErrRequestCannotBeNil,
},
{
"FiatCurrency",
invalidCurrencyCryptoRequest,
errors.New("currency is not a crypto currency, amount must be greater than 0, address cannot be empty"),
},
{
"InvalidAddress",
invalidCryptoAddressRequest,
errors.New(ErrStrAddressisInvalid),
},
}
for _, tests := range testCases {
test := tests
t.Run(test.name, func(t *testing.T) {
err := ValidateCrypto(test.request)
if err != nil {
if test.output.(error).Error() != err.Error() {
t.Fatal(err)
}
}
})
}
}

View File

@@ -1,6 +1,26 @@
package withdraw
import "github.com/thrasher-corp/gocryptotrader/currency"
import (
"errors"
"github.com/thrasher-corp/gocryptotrader/currency"
)
const (
// ErrStrAmountMustBeGreaterThanZero message to return when withdraw amount is less than 0
ErrStrAmountMustBeGreaterThanZero = "amount must be greater than 0"
// ErrStrAddressisInvalid message to return when address is invalid for crypto request
ErrStrAddressisInvalid = "address is not valid"
// ErrStrAddressNotSet message to returh when address is empty
ErrStrAddressNotSet = "address cannot be empty"
// ErrStrNoCurrencySet message to return when no currency is set
ErrStrNoCurrencySet = "currency not set"
)
var (
ErrRequestCannotBeNil = errors.New("request cannot be nil")
ErrInvalidRequest = errors.New("invalid request type")
)
// GenericInfo stores genric withdraw request info
type GenericInfo struct {

File diff suppressed because it is too large Load Diff

View File

@@ -1423,6 +1423,294 @@ func local_request_GoCryptoTrader_GetAuditEvent_0(ctx context.Context, marshaler
}
var (
filter_GoCryptoTrader_GCTScriptExecute_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_GoCryptoTrader_GCTScriptExecute_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptExecuteRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GCTScriptExecute_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptExecute(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptExecute_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptExecuteRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GCTScriptExecute_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptExecute(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptUpload_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptUploadRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptUpload(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptUpload_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptUploadRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptUpload(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptReadScript_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptReadScriptRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptReadScript(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptReadScript_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptReadScriptRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptReadScript(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptStatus_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStatusRequest
var metadata runtime.ServerMetadata
msg, err := client.GCTScriptStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptStatus_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStatusRequest
var metadata runtime.ServerMetadata
msg, err := server.GCTScriptStatus(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_GoCryptoTrader_GCTScriptQuery_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_GoCryptoTrader_GCTScriptQuery_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptQueryRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GCTScriptQuery_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptQuery(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptQuery_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptQueryRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GCTScriptQuery_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptQuery(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptStop_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStopRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptStop(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptStop_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStopRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptStop(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptStopAll_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStopAllRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptStopAll(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptStopAll_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptStopAllRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptStopAll(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptListAll_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptListAllRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptListAll(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptListAll_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptListAllRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptListAll(ctx, &protoReq)
return msg, metadata, err
}
func request_GoCryptoTrader_GCTScriptAutoLoadToggle_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptAutoLoadRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GCTScriptAutoLoadToggle(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_GoCryptoTrader_GCTScriptAutoLoadToggle_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GCTScriptAutoLoadRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GCTScriptAutoLoadToggle(ctx, &protoReq)
return msg, metadata, err
}
// RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux".
// UnaryRPC :call GoCryptoTraderServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -2336,6 +2624,186 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptExecute_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptExecute_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptExecute_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptUpload_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptUpload_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptUpload_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptReadScript_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptReadScript_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptReadScript_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptStatus_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptQuery_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptQuery_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptQuery_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptStop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptStop_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStop_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptStopAll_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptStopAll_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStopAll_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptListAll_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptListAll_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptListAll_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptAutoLoadToggle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_GoCryptoTrader_GCTScriptAutoLoadToggle_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptAutoLoadToggle_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -3337,6 +3805,186 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptExecute_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptExecute_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptExecute_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptUpload_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptUpload_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptUpload_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptReadScript_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptReadScript_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptReadScript_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptStatus_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_GoCryptoTrader_GCTScriptQuery_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptQuery_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptQuery_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptStop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptStop_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStop_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptStopAll_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptStopAll_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptStopAll_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptListAll_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptListAll_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptListAll_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_GoCryptoTrader_GCTScriptAutoLoadToggle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GCTScriptAutoLoadToggle_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GCTScriptAutoLoadToggle_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -3436,6 +4084,24 @@ var (
pattern_GoCryptoTrader_GetExchangeTickerStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangetickerstream"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GetAuditEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getauditevent"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptExecute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "execute"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptUpload_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "upload"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptReadScript_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "read"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "status"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptQuery_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "query"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptStop_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "stop"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptStopAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "stop"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptListAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "stop"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "autoload"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
@@ -3534,4 +4200,22 @@ var (
forward_GoCryptoTrader_GetExchangeTickerStream_0 = runtime.ForwardResponseStream
forward_GoCryptoTrader_GetAuditEvent_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptExecute_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptUpload_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptReadScript_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptStatus_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptQuery_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptStop_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptStopAll_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptListAll_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.ForwardResponseMessage
)

View File

@@ -528,6 +528,62 @@ message AuditEvent {
string timestamp = 4;
}
message GCTScript {
string UUID = 1;
string name = 2;
string path = 3;
string next_run = 4;
}
message GCTScriptExecuteRequest {
GCTScript script = 1;
}
message GCTScriptStopRequest {
GCTScript script = 1;
}
message GCTScriptStopAllRequest{}
message GCTScriptStatusRequest {}
message GCTScriptListAllRequest{}
message GCTScriptUploadRequest {
string script_name = 1;
string script_data = 2;
bytes data = 3;
bool archived = 4;
bool overwrite = 5;
}
message GCTScriptReadScriptRequest{
GCTScript script = 1;
}
message GCTScriptQueryRequest{
GCTScript script = 1;
}
message GCTScriptAutoLoadRequest{
string script = 1;
bool status = 2;
}
message GCTScriptStatusResponse{
string status = 1;
repeated GCTScript scripts = 2;
}
message GCTScriptQueryResponse{
string status = 1;
GCTScript script = 2;
string data = 3;
}
message GCTScriptGenericResponse {
string status = 1;
string data = 2;
}
service GoCryptoTrader {
rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) {
option (google.api.http) = {
@@ -831,13 +887,74 @@ service GoCryptoTrader {
rpc GetExchangeTickerStream(GetExchangeTickerStreamRequest) returns (stream TickerResponse) {
option (google.api.http) = {
get: "/v1/getexchangetickerstream"
get: "/v1/getexchangetickerstream",
};
}
rpc GetAuditEvent(GetAuditEventRequest) returns (GetAuditEventResponse) {
option (google.api.http) = {
get: "/v1/getauditevent"
get: "/v1/getauditevent",
};
}
rpc GCTScriptExecute(GCTScriptExecuteRequest) returns (GCTScriptGenericResponse) {
option (google.api.http) = {
get: "/v1/gctscript/execute",
};
}
rpc GCTScriptUpload(GCTScriptUploadRequest) returns (GCTScriptGenericResponse) {
option (google.api.http) = {
post: "/v1/gctscript/upload",
body: "*"
};
}
rpc GCTScriptReadScript(GCTScriptReadScriptRequest) returns (GCTScriptQueryResponse) {
option (google.api.http) = {
post: "/v1/gctscript/read",
body: "*"
};
}
rpc GCTScriptStatus(GCTScriptStatusRequest) returns (GCTScriptStatusResponse) {
option (google.api.http) = {
get: "/v1/gctscript/status",
};
}
rpc GCTScriptQuery(GCTScriptQueryRequest) returns (GCTScriptQueryResponse) {
option (google.api.http) = {
get: "/v1/gctscript/query",
};
}
rpc GCTScriptStop(GCTScriptStopRequest) returns (GCTScriptGenericResponse) {
option (google.api.http) = {
post: "/v1/gctscript/stop",
body: "*"
};
}
rpc GCTScriptStopAll(GCTScriptStopAllRequest) returns (GCTScriptGenericResponse) {
option (google.api.http) = {
post: "/v1/gctscript/stop",
body: "*"
};
}
rpc GCTScriptListAll(GCTScriptListAllRequest) returns (GCTScriptStatusResponse) {
option (google.api.http) = {
post: "/v1/gctscript/stop",
body: "*"
};
}
rpc GCTScriptAutoLoadToggle(GCTScriptAutoLoadRequest) returns (GCTScriptGenericResponse) {
option (google.api.http) = {
post: "/v1/gctscript/autoload",
body: "*"
};
}
}

View File

@@ -4,10 +4,6 @@
"title": "rpc.proto",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
@@ -271,6 +267,210 @@
]
}
},
"/v1/gctscript/autoload": {
"post": {
"operationId": "GCTScriptAutoLoadToggle",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptGenericResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptAutoLoadRequest"
}
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/execute": {
"get": {
"operationId": "GCTScriptExecute",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptGenericResponse"
}
}
},
"parameters": [
{
"name": "script.UUID",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.name",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.path",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.next_run",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/query": {
"get": {
"operationId": "GCTScriptQuery",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptQueryResponse"
}
}
},
"parameters": [
{
"name": "script.UUID",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.name",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.path",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "script.next_run",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/read": {
"post": {
"operationId": "GCTScriptReadScript",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptQueryResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptReadScriptRequest"
}
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/status": {
"get": {
"operationId": "GCTScriptStatus",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptStatusResponse"
}
}
},
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/stop": {
"post": {
"operationId": "GCTScriptListAll",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptStatusResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptListAllRequest"
}
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/gctscript/upload": {
"post": {
"operationId": "GCTScriptUpload",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptGenericResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/gctrpcGCTScriptUploadRequest"
}
}
],
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/getaccountinfo": {
"get": {
"operationId": "GetAccountInfo",
@@ -1481,6 +1681,119 @@
}
}
},
"gctrpcGCTScript": {
"type": "object",
"properties": {
"UUID": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"next_run": {
"type": "string"
}
}
},
"gctrpcGCTScriptAutoLoadRequest": {
"type": "object",
"properties": {
"script": {
"type": "string"
},
"status": {
"type": "boolean",
"format": "boolean"
}
}
},
"gctrpcGCTScriptGenericResponse": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"data": {
"type": "string"
}
}
},
"gctrpcGCTScriptListAllRequest": {
"type": "object"
},
"gctrpcGCTScriptQueryResponse": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"script": {
"$ref": "#/definitions/gctrpcGCTScript"
},
"data": {
"type": "string"
}
}
},
"gctrpcGCTScriptReadScriptRequest": {
"type": "object",
"properties": {
"script": {
"$ref": "#/definitions/gctrpcGCTScript"
}
}
},
"gctrpcGCTScriptStatusResponse": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"scripts": {
"type": "array",
"items": {
"$ref": "#/definitions/gctrpcGCTScript"
}
}
}
},
"gctrpcGCTScriptStopAllRequest": {
"type": "object"
},
"gctrpcGCTScriptStopRequest": {
"type": "object",
"properties": {
"script": {
"$ref": "#/definitions/gctrpcGCTScript"
}
}
},
"gctrpcGCTScriptUploadRequest": {
"type": "object",
"properties": {
"script_name": {
"type": "string"
},
"script_data": {
"type": "string"
},
"data": {
"type": "string",
"format": "byte"
},
"archived": {
"type": "boolean",
"format": "boolean"
},
"overwrite": {
"type": "boolean",
"format": "boolean"
}
}
},
"gctrpcGenericExchangeNameRequest": {
"type": "object",
"properties": {

262
gctscript/README.md Normal file
View File

@@ -0,0 +1,262 @@
# GoCryptoTrader package gctscript
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/portfolio)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This gctscript package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
## Current Features for gctscript package
+ Execute scripts
+ Terminate scripts
+ Autoload scripts on bot startup
+ Current Exchange features supported:
+ Enabled Exchanges
+ Enabled currency pairs
+ Account information
+ Query Order
+ Submit Order
+ Cancel Order
+ Ticker
+ Orderbook
## How to use
##### Prerequisites
To Enable database logging support you must have an active migrated database by following the [database setup guide](../database/README.md)
##### Configuration
The gctscript configuration struct is currently:
```shell script
type Config struct {
Enabled bool `json:"enabled"`
ScriptTimeout time.Duration `json:"timeout"`
AllowImports bool `json:"allow_imports"`
AutoLoad []string `json:"auto_load"`
Verbose bool `json:"Verbose"`
}
```
With an example configuration being:
```sh
"gctscript": {
"enabled": true,
"timeout": 600000000,
"allow_imports": true,
"auto_load": [],
"debug": false
},
```
##### Script Control
+ You can autoload scripts on bot start up by placing their name in the "auto_load" config entry
```shell script
"auto_load": ["one","two"]
```
This will look in your GoCryptoTrader data directory in a folder called "scripts" for files one.gct and two.gct and autoload them
+ Manual control of scripts can be done via the gctcli command with support for the following:
- Enable/Disable GCTScript:
```shell script
gctcli enablesubsystem "gctscript"
gctcli disablesubsystem "gctscript"
```
- Start/Execute:
```shell script
gctcli gctscript execute <scriptname> <pathoverride>
gctcli gctscript execute "timer.gct" "~/gctscript"
{
"status": "ok",
"data": "timer.gct executed"
}
```
- Stop:
```shell script
gctcli gctscript stop <uuid>
gctcli gctscript stop 821bd73e-02b1-4974-9463-874cb49f130d
{
"status": "ok",
"data": "821bd73e-02b1-4974-9463-874cb49f130d terminated"
}
```
- Status:
```shell script
gctcli gctscript status
{
"status": "ok",
"scripts": [
{
"uuid": "821bd73e-02b1-4974-9463-874cb49f130d",
"name": "timer.gct",
"next_run": "2019-11-14 13:11:40.224919456 +1100 AEDT m=+91.062103259"
}
]
}
```
- Read file:
```shell script
gctcli gctscript read <filename>
gctcli gctscript read "timer.gct"
{
"status": "ok",
"script": {
"name": "timer.gct",
"path": "/home/x/.gocryptotrader/scripts"
},
"data": "fmt := import(\"fmt\")\nt := import(\"times\")\n\nname := \"run\"\ntimer := \"5s\"\n\nload := func() {\n\tfmt.printf(\"5s %s\\n\",t.now())\n}\n\nload()\n"
}
```
- Query running script:
```shell script
gctcli gctscript query <uuid>
gctcli gctscript query 821bd73e-02b1-4974-9463-874cb49f130d
{
"status": "ok",
"script": {
"UUID": "bf692e2d-fa1e-4d95-92fd-33d7634d3d77",
"name": "timer.gct",
"path": "/home/x/.gocryptotrader/scripts",
"next_run": "2019-12-12 07:44:19.747572406 +1100 AEDT m=+16.782773385"
},
"data": "fmt := import(\"fmt\")\nt := import(\"times\")\n\nname := \"run\"\ntimer := \"5s\"\n\nload := func() {\n\tfmt.printf(\"5s %s\\n\",t.now())\n}\n\nload()\n"
}
load()
```
- Add script to autoload:
```shell script
gctcli gctscript autoload add timer
{
"status": "success",
"data": "script timer added to autoload list"
}
```
- Remove script from autoload:
```shell script
gctcli gctscript autoload remove timer
{
"status": "success",
"data": "script timer removed from autoload list"
}
```
##### Scripting & Extending modules
The scripting engine utilises [tengo](https://github.com/d5/tengo) an intro tutorial for it can be found [here](https://github.com/d5/tengo/blob/master/docs/tutorial.md)
Modules have been written so far linking up common exchange features including
- Orderbook
- Ticker
- Order Management
- Account information
- Withdraw funds
- Get Deposit Addresses
Extending or creating new modules:
Extending an existing module the exchange module for example is simple
- Open required [module](modules/gct/exchange.go)
- Add to exchangeModule map
- Define function with signature ```(args ...objects.Object) (ret objects.Object, err error)```
Similar steps can be taken to add a new module with a few adjustments
- Open required [GCT](modules/gct/gct_types.go)
- Add module name to GCTModules map
##### GCT module methods
Current supported methods added and exposed to scripts are as follows:
```
accountinfo
-> exchange:string
depositaddress
-> exchange:string
-> currency:string
orderbook
-> exchange:string
-> currency pair:string
-> delimiter:string
-> asset:string
ticker
-> exchange:string
-> currency pair:string
-> delimiter:string
-> asset:string
pairs
-> exchange:string
-> enabled only:bool
-> asset:string
queryorder
-> exchange:string
-> order id:string
submitorder
-> exchange:string
-> currency pair:string
-> delimiter:string
-> order type:string
-> order side:string
-> price:float64
-> amount:float64
-> client_id:string
withdrawfiat
-> exchange:string
-> currency:string
-> description:string
-> amount:float64
-> bank id:string
withdrawcrypto
-> exchange:string
-> currency:string
-> address:string
-> address tag:string
-> amount:float64
-> fee:float64
-> description:string
```
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

View File

@@ -0,0 +1,13 @@
// import fmt package
fmt := import("fmt")
// import exchange package
exch := import("exchange")
load := func() {
// retrieve account information from exchange and store in info variable
info := exch.accountinfo("BTC Markets")
// print out info
fmt.print(info)
}
load()

View File

@@ -0,0 +1,9 @@
fmt := import("fmt")
exch := import("exchange")
load := func() {
info := exch.depositaddress("BTC Markets", "BTC", "")
fmt.println(info)
}
load()

View File

@@ -0,0 +1,12 @@
fmt := import("fmt")
exch := import("exchange")
name := "run"
timer := "5s"
load := func() {
tx := exch.orderbook("btc markets", "btc-aud", "-", "spot")
fmt.println(tx)
}
load()

View File

@@ -0,0 +1,9 @@
fmt := import("fmt")
exch := import("exchange")
load := func() {
info := exch.pairs("BTC Markets", false, "SPOT")
fmt.print(info)
}
load()

View File

@@ -0,0 +1,9 @@
fmt := import("fmt")
exch := import("exchange")
load := func() {
info := exch.orderquery("BTC Markets", "4491600698")
fmt.print(info)
}
load()

View File

@@ -0,0 +1,9 @@
fmt := import("fmt")
exch := import("exchange")
load := func() {
info := exch.ordersubmit("BTC Markets","BTC-AUD","-","LIMIT","SELL",1000000, 1,"")
fmt.print(info)
}
load()

View File

@@ -0,0 +1,13 @@
fmt := import("fmt")
exch := import("exchange")
name := "run"
timer := "5s"
load := func() {
tx := exch.ticker("btc markets", "btc-aud", "-", "spot")
fmt.println(tx)
}
load()

View File

@@ -0,0 +1,25 @@
// import fmt package
fmt := import("fmt")
// import exchange package
exch := import("exchange")
load := func() {
// retrieve account information from exchange and store in info variable
// withdrawcrypto
// 1: Exchange name
// 2: currency
// 3: address
// 4: address tag
// 5: amount
// 6: fee amount
// 7: trade password
// 8: OTP
// submit request to withdraw funds
info := exch.withdrawfiat("BTC Markets", "AUD", "hello", 1, "-")
// print out info
fmt.print(info)
}
load()

View File

@@ -0,0 +1,24 @@
// import fmt package
fmt := import("fmt")
// import exchange package
exch := import("exchange")
load := func() {
// retrieve account information from exchange and store in info variable
// withdrawcrypto
// 1: Exchange name
// 2: currency
// 3: address
// 4: address tag
// 5: amount
// 6: fee amount
// 7: trade password
// 8: OTP
info := exch.withdrawcrypto("BTC Markets","BTC", "1234562362", "1231", 1.0, 0.0, "","" )
// print out info
fmt.print(info)
}
load()

View File

@@ -0,0 +1,16 @@
fmt := import("fmt")
timer := "5s"
exit := func() {
timer = 0
}
load := func() {
for x := 0 ; x < 20; x++ {
fmt.printf("Hello %v", x)
}
exit()
}
load()

11
gctscript/gctscript.go Normal file
View File

@@ -0,0 +1,11 @@
package gctscript
import (
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/gct"
)
// Setup configures the wrapper interface to use
func Setup() {
modules.SetModuleWrapper(gct.Setup())
}

View File

@@ -0,0 +1,498 @@
package gct
import (
"fmt"
"strings"
objects "github.com/d5/tengo/v2"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers"
)
var exchangeModule = map[string]objects.Object{
"orderbook": &objects.UserFunction{Name: "orderbook", Value: ExchangeOrderbook},
"ticker": &objects.UserFunction{Name: "ticker", Value: ExchangeTicker},
"exchanges": &objects.UserFunction{Name: "exchanges", Value: ExchangeExchanges},
"pairs": &objects.UserFunction{Name: "pairs", Value: ExchangePairs},
"accountinfo": &objects.UserFunction{Name: "accountinfo", Value: ExchangeAccountInfo},
"depositaddress": &objects.UserFunction{Name: "depositaddress", Value: ExchangeDepositAddress},
"orderquery": &objects.UserFunction{Name: "orderquery", Value: ExchangeOrderQuery},
"ordercancel": &objects.UserFunction{Name: "ordercancel", Value: ExchangeOrderCancel},
"ordersubmit": &objects.UserFunction{Name: "ordersubmit", Value: ExchangeOrderSubmit},
"withdrawcrypto": &objects.UserFunction{Name: "withdrawcrypto", Value: ExchangeWithdrawCrypto},
"withdrawfiat": &objects.UserFunction{Name: "withdrawfiat", Value: ExchangeWithdrawFiat},
}
// ExchangeOrderbook returns orderbook for requested exchange & currencypair
func ExchangeOrderbook(args ...objects.Object) (objects.Object, error) {
if len(args) != 4 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
currencyPair, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
}
delimiter, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
}
assetTypeParam, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
}
pairs := currency.NewPairDelimiter(currencyPair, delimiter)
assetType := asset.Item(assetTypeParam)
ob, err := wrappers.GetWrapper().Orderbook(exchangeName, pairs, assetType)
if err != nil {
return nil, err
}
var asks, bids objects.Array
for x := range ob.Asks {
temp := make(map[string]objects.Object, 2)
temp["amount"] = &objects.Float{Value: ob.Asks[x].Amount}
temp["price"] = &objects.Float{Value: ob.Asks[x].Price}
asks.Value = append(asks.Value, &objects.Map{Value: temp})
}
for x := range ob.Bids {
temp := make(map[string]objects.Object, 2)
temp["amount"] = &objects.Float{Value: ob.Bids[x].Amount}
temp["price"] = &objects.Float{Value: ob.Bids[x].Price}
bids.Value = append(bids.Value, &objects.Map{Value: temp})
}
data := make(map[string]objects.Object, 5)
data["exchange"] = &objects.String{Value: ob.ExchangeName}
data["pair"] = &objects.String{Value: ob.Pair.String()}
data["asks"] = &asks
data["bids"] = &bids
data["asset"] = &objects.String{Value: ob.AssetType.String()}
return &objects.Map{
Value: data,
}, nil
}
// ExchangeTicker returns ticker data for requested exchange and currency pair
func ExchangeTicker(args ...objects.Object) (objects.Object, error) {
if len(args) != 4 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
currencyPair, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
}
delimiter, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
}
assetTypeParam, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
}
pairs := currency.NewPairDelimiter(currencyPair, delimiter)
assetType := asset.Item(assetTypeParam)
tx, err := wrappers.GetWrapper().Ticker(exchangeName, pairs, assetType)
if err != nil {
return nil, err
}
data := make(map[string]objects.Object, 14)
data["exchange"] = &objects.String{Value: tx.ExchangeName}
data["last"] = &objects.Float{Value: tx.Last}
data["High"] = &objects.Float{Value: tx.High}
data["Low"] = &objects.Float{Value: tx.Low}
data["bid"] = &objects.Float{Value: tx.Bid}
data["ask"] = &objects.Float{Value: tx.Ask}
data["volume"] = &objects.Float{Value: tx.Volume}
data["quotevolume"] = &objects.Float{Value: tx.QuoteVolume}
data["priceath"] = &objects.Float{Value: tx.PriceATH}
data["open"] = &objects.Float{Value: tx.Open}
data["close"] = &objects.Float{Value: tx.Close}
data["pair"] = &objects.String{Value: tx.Pair.String()}
data["asset"] = &objects.String{Value: tx.AssetType.String()}
data["updated"] = &objects.Time{Value: tx.LastUpdated}
return &objects.Map{
Value: data,
}, nil
}
// ExchangeExchanges returns list of exchanges either enabled or all
func ExchangeExchanges(args ...objects.Object) (objects.Object, error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
enabledOnly, ok := objects.ToBool(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, enabledOnly)
}
rtnValue := wrappers.GetWrapper().Exchanges(enabledOnly)
r := objects.Array{}
for x := range rtnValue {
r.Value = append(r.Value, &objects.String{Value: rtnValue[x]})
}
return &r, nil
}
// ExchangePairs returns currency pairs for requested exchange
func ExchangePairs(args ...objects.Object) (objects.Object, error) {
if len(args) != 3 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
enabledOnly, ok := objects.ToBool(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, enabledOnly)
}
assetTypeParam, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
}
assetType := asset.Item(strings.ToLower(assetTypeParam))
rtnValue, err := wrappers.GetWrapper().Pairs(exchangeName, enabledOnly, assetType)
if err != nil {
return nil, err
}
r := objects.Array{}
for x := range rtnValue.Slice() {
r.Value = append(r.Value, &objects.String{Value: rtnValue.Slice()[x].String()})
}
return &r, nil
}
// ExchangeAccountInfo returns account information for requested exchange
func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
rtnValue, err := wrappers.GetWrapper().AccountInformation(exchangeName)
if err != nil {
return nil, err
}
var funds objects.Array
for x := range rtnValue.Accounts {
for y := range rtnValue.Accounts[x].Currencies {
temp := make(map[string]objects.Object, 3)
temp["name"] = &objects.String{Value: rtnValue.Accounts[x].Currencies[y].CurrencyName.String()}
temp["total"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].TotalValue}
temp["hold"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].Hold}
funds.Value = append(funds.Value, &objects.Map{Value: temp})
}
}
data := make(map[string]objects.Object, 2)
data["exchange"] = &objects.String{Value: rtnValue.Exchange}
data["currencies"] = &funds
return &objects.Map{
Value: data,
}, nil
}
// ExchangeOrderQuery query order on exchange
func ExchangeOrderQuery(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
orderID, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderID)
}
orderDetails, err := wrappers.GetWrapper().QueryOrder(exchangeName, orderID)
if err != nil {
return nil, err
}
var tradeHistory objects.Array
for x := range orderDetails.Trades {
temp := make(map[string]objects.Object, 7)
temp["timestamp"] = &objects.Time{Value: orderDetails.Trades[x].Timestamp}
temp["price"] = &objects.Float{Value: orderDetails.Trades[x].Price}
temp["fee"] = &objects.Float{Value: orderDetails.Trades[x].Fee}
temp["amount"] = &objects.Float{Value: orderDetails.Trades[x].Amount}
temp["type"] = &objects.String{Value: orderDetails.Trades[x].Type.String()}
temp["side"] = &objects.String{Value: orderDetails.Trades[x].Side.String()}
temp["description"] = &objects.String{Value: orderDetails.Trades[x].Description}
tradeHistory.Value = append(tradeHistory.Value, &objects.Map{Value: temp})
}
data := make(map[string]objects.Object, 14)
data["exchange"] = &objects.String{Value: orderDetails.Exchange}
data["id"] = &objects.String{Value: orderDetails.ID}
data["accountid"] = &objects.String{Value: orderDetails.AccountID}
data["currencypair"] = &objects.String{Value: orderDetails.CurrencyPair.String()}
data["price"] = &objects.Float{Value: orderDetails.Price}
data["amount"] = &objects.Float{Value: orderDetails.Amount}
data["amountexecuted"] = &objects.Float{Value: orderDetails.ExecutedAmount}
data["amountremaining"] = &objects.Float{Value: orderDetails.RemainingAmount}
data["fee"] = &objects.Float{Value: orderDetails.Fee}
data["side"] = &objects.String{Value: orderDetails.OrderSide.String()}
data["type"] = &objects.String{Value: orderDetails.OrderType.String()}
data["date"] = &objects.String{Value: orderDetails.OrderDate.String()}
data["status"] = &objects.String{Value: orderDetails.Status.String()}
data["trades"] = &tradeHistory
return &objects.Map{
Value: data,
}, nil
}
// ExchangeOrderCancel cancels order on requested exchange
func ExchangeOrderCancel(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
orderID, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderID)
}
rtn, err := wrappers.GetWrapper().CancelOrder(exchangeName, orderID)
if err != nil {
return nil, err
}
if rtn {
return objects.TrueValue, nil
}
return objects.FalseValue, nil
}
// ExchangeOrderSubmit submit order on exchange
func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) {
if len(args) != 8 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
currencyPair, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
}
delimiter, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
}
orderType, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderType)
}
orderSide, ok := objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderSide)
}
orderPrice, ok := objects.ToFloat64(args[5])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderPrice)
}
orderAmount, ok := objects.ToFloat64(args[6])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderAmount)
}
orderClientID, ok := objects.ToString(args[7])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderClientID)
}
pair := currency.NewPairDelimiter(currencyPair, delimiter)
tempSubmit := &order.Submit{
Pair: pair,
OrderType: order.Type(orderType),
OrderSide: order.Side(orderSide),
Price: orderPrice,
Amount: orderAmount,
ClientID: orderClientID,
}
err := tempSubmit.Validate()
if err != nil {
return nil, err
}
rtn, err := wrappers.GetWrapper().SubmitOrder(exchangeName, tempSubmit)
if err != nil {
return nil, err
}
data := make(map[string]objects.Object, 2)
data["orderid"] = &objects.String{Value: rtn.OrderID}
if rtn.IsOrderPlaced {
data["isorderplaced"] = objects.TrueValue
} else {
data["isorderplaced"] = objects.FalseValue
}
return &objects.Map{
Value: data,
}, nil
}
// ExchangeDepositAddress returns deposit address (if supported by exchange)
func ExchangeDepositAddress(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
currencyCode, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyCode)
}
currCode := currency.NewCode(currencyCode)
rtn, err := wrappers.GetWrapper().DepositAddress(exchangeName, currCode)
if err != nil {
return nil, err
}
return &objects.String{Value: rtn}, nil
}
// ExchangeWithdrawCrypto submit request to withdraw crypto assets
func ExchangeWithdrawCrypto(args ...objects.Object) (objects.Object, error) {
if len(args) != 7 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
cur, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, cur)
}
address, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, address)
}
addressTag, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, addressTag)
}
amount, ok := objects.ToFloat64(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, amount)
}
feeAmount, ok := objects.ToFloat64(args[5])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, feeAmount)
}
description, ok := objects.ToString(args[6])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, description)
}
withdrawRequest := &withdraw.CryptoRequest{
GenericInfo: withdraw.GenericInfo{
Currency: currency.NewCode(cur),
Description: description,
Amount: amount,
},
Address: address,
AddressTag: addressTag,
FeeAmount: feeAmount,
}
rtn, err := wrappers.GetWrapper().WithdrawalCryptoFunds(exchangeName, withdrawRequest)
if err != nil {
return nil, err
}
return &objects.String{Value: rtn}, nil
}
// ExchangeWithdrawFiat submit request to withdraw fiat assets
func ExchangeWithdrawFiat(args ...objects.Object) (objects.Object, error) {
if len(args) != 5 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
}
cur, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, cur)
}
description, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, description)
}
amount, ok := objects.ToFloat64(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, amount)
}
bankAccountID, ok := objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, bankAccountID)
}
withdrawRequest := &withdraw.FiatRequest{
GenericInfo: withdraw.GenericInfo{
Currency: currency.NewCode(cur),
Description: description,
Amount: amount,
},
}
rtn, err := wrappers.GetWrapper().WithdrawalFiatFunds(exchangeName, bankAccountID, withdrawRequest)
if err != nil {
return nil, err
}
return &objects.String{Value: rtn}, nil
}

View File

@@ -0,0 +1,10 @@
package gct
// AllModuleNames returns a list of all default module names.
func AllModuleNames() []string {
var names []string
for name := range Modules {
names = append(names, name)
}
return names
}

View File

@@ -0,0 +1,270 @@
package gct
import (
"errors"
"os"
"reflect"
"testing"
objects "github.com/d5/tengo/v2"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
)
var (
exch = &objects.String{
Value: "BTC Markets",
}
exchError = &objects.String{
Value: "error",
}
currencyPair = &objects.String{
Value: "BTC-AUD",
}
delimiter = &objects.String{
Value: "-",
}
assetType = &objects.String{
Value: "SPOT",
}
orderID = &objects.String{
Value: "1235",
}
tv = objects.TrueValue
fv = objects.FalseValue
errTestFailed = errors.New("test failed")
)
func TestMain(m *testing.M) {
modules.SetModuleWrapper(validator.Wrapper{})
os.Exit(m.Run())
}
func TestExchangeOrderbook(t *testing.T) {
t.Parallel()
_, err := ExchangeOrderbook(exch, currencyPair, delimiter, assetType)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeOrderbook(exchError, currencyPair, delimiter, assetType)
if err != nil && errors.Is(err, errTestFailed) {
t.Fatal(err)
}
_, err = ExchangeOrderbook()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
}
func TestExchangeTicker(t *testing.T) {
t.Parallel()
_, err := ExchangeTicker(exch, currencyPair, delimiter, assetType)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeTicker(exchError, currencyPair, delimiter, assetType)
if err != nil && errors.Is(err, errTestFailed) {
t.Fatal(err)
}
_, err = ExchangeTicker()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
}
func TestExchangeExchanges(t *testing.T) {
t.Parallel()
_, err := ExchangeExchanges(tv)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeExchanges(exch)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeExchanges(fv)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeExchanges()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
}
func TestExchangePairs(t *testing.T) {
t.Parallel()
_, err := ExchangePairs(exch, tv, assetType)
if err != nil {
t.Fatal(err)
}
_, err = ExchangePairs(exchError, tv, assetType)
if err != nil && errors.Is(err, errTestFailed) {
t.Fatal(err)
}
_, err = ExchangePairs()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
}
func TestAccountInfo(t *testing.T) {
t.Parallel()
_, err := ExchangeAccountInfo()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
_, err = ExchangeAccountInfo(exch)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeAccountInfo(exchError)
if err != nil && !errors.Is(err, errTestFailed) {
t.Fatal(err)
}
}
func TestExchangeOrderQuery(t *testing.T) {
t.Parallel()
_, err := ExchangeOrderQuery()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
_, err = ExchangeOrderQuery(exch, orderID)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeOrderQuery(exchError, orderID)
if err != nil && !errors.Is(err, errTestFailed) {
t.Fatal(err)
}
}
func TestExchangeOrderCancel(t *testing.T) {
_, err := ExchangeOrderCancel()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
_, err = ExchangeOrderCancel(exch, orderID)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeOrderCancel(exch, objects.FalseValue)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeOrderCancel(exchError, orderID)
if err != nil && !errors.Is(err, errTestFailed) {
t.Fatal(err)
}
}
func TestExchangeOrderSubmit(t *testing.T) {
_, err := ExchangeOrderSubmit()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
orderSide := &objects.String{Value: "ASK"}
orderType := &objects.String{Value: "LIMIT"}
orderPrice := &objects.Float{Value: 1}
orderAmount := &objects.Float{Value: 1}
_, err = ExchangeOrderSubmit(exch, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID)
if err != nil && !errors.Is(err, errTestFailed) {
t.Fatal(err)
}
_, err = ExchangeOrderSubmit(exch, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeOrderSubmit(objects.TrueValue, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID)
if err != nil {
t.Fatal(err)
}
}
func TestAllModuleNames(t *testing.T) {
x := AllModuleNames()
xType := reflect.TypeOf(x).Kind()
if xType != reflect.Slice {
t.Fatalf("AllModuleNames() should return slice instead received: %v", x)
}
}
func TestExchangeDepositAddress(t *testing.T) {
_, err := ExchangeDepositAddress()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
currCode := &objects.String{Value: "BTC"}
_, err = ExchangeDepositAddress(exch, currCode)
if err != nil {
t.Fatal(err)
}
_, err = ExchangeDepositAddress(exchError, currCode)
if err != nil && !errors.Is(err, errTestFailed) {
t.Fatal(err)
}
}
func TestExchangeWithdrawCrypto(t *testing.T) {
_, err := ExchangeWithdrawCrypto()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
currCode := &objects.String{Value: "BTC"}
desc := &objects.String{Value: "HELLO"}
address := &objects.String{Value: "0xTHISISALEGITBTCADDRESSS"}
amount := &objects.Float{Value: 1.0}
_, err = ExchangeWithdrawCrypto(exch, currCode, address, address, amount, amount, desc)
if err != nil {
t.Fatal(err)
}
}
func TestExchangeWithdrawFiat(t *testing.T) {
_, err := ExchangeWithdrawFiat()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatal(err)
}
currCode := &objects.String{Value: "AUD"}
desc := &objects.String{Value: "Hello"}
amount := &objects.Float{Value: 1.0}
bankID := &objects.String{Value: "test-bank-01"}
_, err = ExchangeWithdrawFiat(exch, currCode, desc, amount, bankID)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,15 @@
package gct
import (
"github.com/d5/tengo/v2"
)
const (
// ErrParameterConvertFailed error to return when type conversion fails
ErrParameterConvertFailed = "%v failed conversion"
)
// Modules map of all loadable modules
var Modules = map[string]map[string]tengo.Object{
"exchange": exchangeModule,
}

View File

@@ -0,0 +1,32 @@
package loader
import (
"github.com/d5/tengo/v2"
"github.com/d5/tengo/v2/stdlib"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules/gct"
)
// GetModuleMap returns the module map that includes all modules
// for the given module names.
func GetModuleMap() *tengo.ModuleMap {
modules := tengo.NewModuleMap()
gctModuleList := gct.AllModuleNames()
for _, name := range gctModuleList {
if mod := gct.Modules[name]; mod != nil {
modules.AddBuiltinModule(name, mod)
}
}
stdLib := stdlib.AllModuleNames()
for _, name := range stdLib {
if mod := stdlib.BuiltinModules[name]; mod != nil {
modules.AddBuiltinModule(name, mod)
}
if mod := stdlib.SourceModules[name]; mod != "" {
modules.AddSourceModule(name, []byte(mod))
}
}
return modules
}

View File

@@ -0,0 +1,18 @@
package loader
import (
"reflect"
"testing"
)
func TestGetModuleMap(t *testing.T) {
x := GetModuleMap()
xType := reflect.TypeOf(x).String()
if xType != "*tengo.ModuleMap" {
t.Fatalf("GetModuleMap() should return pointer to ModuleMap instead received: %v", x)
}
if x.Len() == 0 {
t.Fatal("expected GetModuleMap() to contain module results instead received 0 value")
}
}

View File

@@ -0,0 +1,59 @@
package modules
import (
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
)
// Wrapper instance of GCT to use for modules
var Wrapper GCT
// GCT interface requirements
type GCT interface {
Exchange
}
// Exchange interface requirements
type Exchange interface {
Exchanges(enabledOnly bool) []string
IsEnabled(exch string) bool
Orderbook(exch string, pair currency.Pair, item asset.Item) (*orderbook.Base, error)
Ticker(exch string, pair currency.Pair, item asset.Item) (*ticker.Price, error)
Pairs(exch string, enabledOnly bool, item asset.Item) (*currency.Pairs, error)
QueryOrder(exch, orderid string) (*order.Detail, error)
SubmitOrder(exch string, submit *order.Submit) (*order.SubmitResponse, error)
CancelOrder(exch, orderid string) (bool, error)
AccountInformation(exch string) (*AccountInfo, error)
DepositAddress(exch string, currencyCode currency.Code) (string, error)
WithdrawalFiatFunds(exch, bankaccountid string, request *withdraw.FiatRequest) (out string, err error)
WithdrawalCryptoFunds(exch string, request *withdraw.CryptoRequest) (out string, err error)
}
// SetModuleWrapper link the wrapper and interface to use for modules
func SetModuleWrapper(wrapper GCT) {
Wrapper = wrapper
}
// AccountInfo is a Generic type to hold each exchange's holdings in
// all enabled currencies
type AccountInfo struct {
Exchange string
Accounts []Account
}
// Account defines a singular account type with associated currencies
type Account struct {
ID string
Currencies []AccountCurrencyInfo
}
// AccountCurrencyInfo is a sub type to store currency name and value
type AccountCurrencyInfo struct {
CurrencyName currency.Code
TotalValue float64
Hold float64
}

43
gctscript/vm/autoload.go Normal file
View File

@@ -0,0 +1,43 @@
package vm
import (
"fmt"
"os"
"path/filepath"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// Autoload remove entry from autoload slice
func Autoload(name string, remove bool) error {
if filepath.Ext(name) != ".gct" {
name += ".gct"
}
if remove {
for x := range GCTScriptConfig.AutoLoad {
if GCTScriptConfig.AutoLoad[x] != name {
continue
}
GCTScriptConfig.AutoLoad = append(GCTScriptConfig.AutoLoad[:x], GCTScriptConfig.AutoLoad[x+1:]...)
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Removing script: %s from autoload", name)
}
return nil
}
return fmt.Errorf("%v - not found", name)
}
script := filepath.Join(ScriptPath, name)
_, err := os.Stat(script)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("%v - not found", script)
}
return err
}
GCTScriptConfig.AutoLoad = append(GCTScriptConfig.AutoLoad, name)
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Adding script: %s to autoload", name)
}
return nil
}

81
gctscript/vm/gctscript.go Normal file
View File

@@ -0,0 +1,81 @@
package vm
import (
"fmt"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// New returns a new instance of VM
func New() *VM {
if VMSCount.Len() >= int32(GCTScriptConfig.MaxVirtualMachines) {
if GCTScriptConfig.Verbose {
log.Warnf(log.GCTScriptMgr, "GCTScript MaxVirtualMachines (%v) hit, unable to start further instances",
GCTScriptConfig.MaxVirtualMachines)
}
return nil
}
VMSCount.add()
vm := NewVM()
if vm == nil {
VMSCount.remove()
} else {
AllVMSync.Store(vm.ID, vm)
}
return vm
}
// Validate will attempt to execute a script in a test/non-live environment
// to confirm it passes requirements for execution
func Validate(file string) (err error) {
validator.IsTestExecution.Store(true)
defer validator.IsTestExecution.Store(false)
tempVM := NewVM()
err = tempVM.Load(file)
if err != nil {
return
}
err = tempVM.Compile()
if err != nil {
return
}
return tempVM.Run()
}
// ShutdownAll shutdown all
func ShutdownAll() (err error) {
if GCTScriptConfig.Verbose {
log.Debugln(log.GCTScriptMgr, "Shutting down all Virtual Machines")
}
var errors []error
AllVMSync.Range(func(k, v interface{}) bool {
errShutdown := v.(*VM).Shutdown()
if err != nil {
errors = append(errors, errShutdown)
}
return true
})
if len(errors) > 0 {
err = fmt.Errorf("failed to shutdown the following Virtual Machines: %v", errors)
}
return err
}
// RemoveVM remove VM from list
func RemoveVM(id uuid.UUID) error {
if _, f := AllVMSync.Load(id); !f {
return fmt.Errorf(ErrNoVMFound, id.String())
}
AllVMSync.Delete(id)
VMSCount.remove()
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "VM %v removed from AllVMs", id)
}
return nil
}

View File

@@ -0,0 +1,39 @@
package vm
import (
"errors"
"time"
)
const gctScript = "GCT Script"
// Config user configurable options for gctscript
type Config struct {
Enabled bool `json:"enabled"`
ScriptTimeout time.Duration `json:"timeout"`
MaxVirtualMachines uint8 `json:"max_virtual_machines"`
AllowImports bool `json:"allow_imports"`
AutoLoad []string `json:"auto_load"`
Verbose bool `json:"verbose"`
}
// Error interface to meet error requirements
type Error struct {
Script string
Action string
Cause error
}
var (
// GCTScriptConfig initialised global copy of Config{}
GCTScriptConfig = &Config{}
// ScriptPath path to load/save scripts
ScriptPath string
)
var (
// ErrScriptingDisabled error message displayed when gctscript is disabled
ErrScriptingDisabled = errors.New("scripting is disabled")
// ErrNoVMLoaded error message displayed if a virtual machine has not been initialised
ErrNoVMLoaded = errors.New("no virtual machine loaded")
)

302
gctscript/vm/vm.go Normal file
View File

@@ -0,0 +1,302 @@
package vm
import (
"archive/zip"
"bytes"
"context"
"encoding/hex"
"io/ioutil"
"os"
"path/filepath"
"sync/atomic"
"time"
"github.com/d5/tengo/v2"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
scriptevent "github.com/thrasher-corp/gocryptotrader/database/repository/script"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules/loader"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/volatiletech/null"
)
// NewVM attempts to create a new Virtual Machine firstly from pool
func NewVM() (vm *VM) {
newUUID, err := uuid.NewV4()
if err != nil {
log.Error(log.GCTScriptMgr, Error{
Action: "New: UUID",
Cause: err,
})
return nil
}
if GCTScriptConfig.Verbose {
log.Debugln(log.GCTScriptMgr, "New GCTScript VM created")
}
vm = &VM{
ID: newUUID,
Script: pool.Get().(*tengo.Script),
}
return
}
// Load parses and creates a new instance of tengo script vm
func (vm *VM) Load(file string) error {
if vm == nil {
return ErrNoVMLoaded
}
if !GCTScriptConfig.Enabled {
return &Error{
Action: "Load",
Cause: ErrScriptingDisabled,
}
}
if filepath.Ext(file) != ".gct" {
file += ".gct"
}
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Loading script: %s ID: %v", vm.ShortName(), vm.ID)
}
f, err := os.Open(file)
if err != nil {
return &Error{
Action: "Load: Open",
Script: file,
Cause: err,
}
}
defer f.Close()
code, err := ioutil.ReadAll(f)
if err != nil {
return &Error{
Action: "Load: Read",
Script: file,
Cause: err,
}
}
vm.File = file
vm.Path = filepath.Dir(file)
vm.Script = tengo.NewScript(code)
vm.Script.SetImports(loader.GetModuleMap())
vm.Hash = vm.getHash()
if GCTScriptConfig.AllowImports {
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "File imports enabled for vm: %v", vm.ID)
}
vm.Script.EnableFileImport(true)
}
vm.event(StatusSuccess, TypeLoad)
return nil
}
// Compile compiles to byte code loaded copy of vm script
func (vm *VM) Compile() (err error) {
vm.Compiled = new(tengo.Compiled)
vm.Compiled, err = vm.Script.Compile()
return
}
// Run runs byte code
func (vm *VM) Run() (err error) {
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Running script: %s ID: %v", vm.ShortName(), vm.ID)
}
err = vm.Compiled.Run()
if err != nil {
vm.event(StatusFailure, TypeExecute)
return Error{
Action: "Run",
Cause: err,
}
}
vm.event(StatusSuccess, TypeExecute)
return
}
// RunCtx runs compiled byte code with context.Context support.
func (vm *VM) RunCtx() (err error) {
if vm.ctx == nil {
vm.ctx = context.Background()
}
ct, cancel := context.WithTimeout(vm.ctx, GCTScriptConfig.ScriptTimeout)
defer cancel()
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Running script: %s ID: %v", vm.ShortName(), vm.ID)
}
err = vm.Compiled.RunContext(ct)
if err != nil {
vm.event(StatusFailure, TypeExecute)
return Error{
Action: "RunCtx",
Cause: err,
}
}
vm.event(StatusSuccess, TypeExecute)
return
}
// CompileAndRun Compile and Run script with support for task running
func (vm *VM) CompileAndRun() {
if vm == nil {
return
}
err := vm.Compile()
if err != nil {
log.Error(log.GCTScriptMgr, err)
err = RemoveVM(vm.ID)
if err != nil {
log.Error(log.GCTScriptMgr, err)
}
return
}
err = vm.RunCtx()
if err != nil {
log.Error(log.GCTScriptMgr, err)
err = RemoveVM(vm.ID)
if err != nil {
log.Error(log.GCTScriptMgr, err)
}
return
}
if vm.Compiled.Get("timer").String() != "" {
vm.T, err = time.ParseDuration(vm.Compiled.Get("timer").String())
if err != nil {
log.Error(log.GCTScriptMgr, err)
err = vm.Shutdown()
if err != nil {
log.Error(log.GCTScriptMgr, err)
}
return
}
if vm.T < time.Nanosecond {
log.Error(log.GCTScriptMgr, "Repeat timer cannot be under 1 nano second")
err = vm.Shutdown()
if err != nil {
log.Errorln(log.GCTScriptMgr, err)
}
return
}
vm.runner()
} else {
err = vm.Shutdown()
if err != nil {
log.Error(log.GCTScriptMgr, err)
}
return
}
}
// Shutdown shuts down current VM
func (vm *VM) Shutdown() error {
if vm == nil {
return ErrNoVMLoaded
}
if vm.S != nil {
close(vm.S)
}
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Shutting down script: %s ID: %v", vm.ShortName(), vm.ID)
}
vm.Script = nil
pool.Put(vm.Script)
vm.event(StatusSuccess, TypeStop)
return RemoveVM(vm.ID)
}
// Read contents of script back and create script event
func (vm *VM) Read() ([]byte, error) {
vm.event(StatusSuccess, TypeRead)
return vm.read()
}
// Read contents of script back
func (vm *VM) read() ([]byte, error) {
if GCTScriptConfig.Verbose {
log.Debugf(log.GCTScriptMgr, "Read script: %s ID: %v", vm.ShortName(), vm.ID)
}
return ioutil.ReadFile(vm.File)
}
// ShortName returns short (just filename.extension) of running script
func (vm *VM) ShortName() string {
return filepath.Base(vm.File)
}
func (vm *VM) event(status, executionType string) {
if validator.IsTestExecution.Load() == true {
return
}
var data null.Bytes
if executionType == TypeLoad {
scriptData, err := vm.scriptData()
if err != nil {
log.Errorf(log.GCTScriptMgr, "Failed to retrieve scriptData: %v", err)
}
data.SetValid(scriptData)
}
scriptevent.Event(vm.getHash(), vm.ShortName(), vm.Path, data, executionType, status, time.Now())
}
func (vm *VM) scriptData() ([]byte, error) {
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
f, err := w.Create(vm.ShortName())
if err != nil {
return []byte{}, err
}
contents, err := vm.read()
if err != nil {
return []byte{}, err
}
_, err = f.Write(contents)
if err != nil {
return []byte{}, err
}
err = w.Close()
if err != nil {
return []byte{}, err
}
return buf.Bytes(), nil
}
func (vm *VM) getHash() string {
if vm.Hash != "" {
return vm.Hash
}
contents, err := vm.read()
if err != nil {
log.Errorln(log.GCTScriptMgr, err)
}
contents = append(contents, vm.ShortName()...)
return hex.EncodeToString(crypto.GetSHA256(contents))
}
func (vmc *vmscount) add() {
atomic.AddInt32((*int32)(vmc), 1)
}
func (vmc *vmscount) remove() {
atomic.AddInt32((*int32)(vmc), -1)
}
// Len() returns current length vmscount
func (vmc *vmscount) Len() int32 {
return atomic.LoadInt32((*int32)(vmc))
}

29
gctscript/vm/vm_error.go Normal file
View File

@@ -0,0 +1,29 @@
package vm
import (
"fmt"
"path/filepath"
)
const (
// ErrNoVMFound error returned when no virtual machine is found
ErrNoVMFound = "VM %v not found"
)
func (e Error) Error() string {
var scriptName, action string
if e.Script != "" {
scriptName = fmt.Sprintf("(SCRIPT) %s ", filepath.Base(e.Script))
}
if e.Action != "" {
action = fmt.Sprintf("(ACTION) %s ", e.Action)
}
return fmt.Sprintf("%s: %s%s%s", gctScript, action, scriptName, e.Cause)
}
// Unwrap returns e.Cause meeting errors interface requirements.
func (e Error) Unwrap() error {
return e.Cause
}

30
gctscript/vm/vm_task.go Normal file
View File

@@ -0,0 +1,30 @@
package vm
import (
"time"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
func (vm *VM) runner() {
vm.S = make(chan struct{}, 1)
waitTime := time.NewTicker(vm.T)
vm.NextRun = time.Now().Add(vm.T)
go func() {
for {
select {
case <-waitTime.C:
vm.NextRun = time.Now().Add(vm.T)
err := vm.RunCtx()
if err != nil {
log.Error(log.GCTScriptMgr, err)
return
}
case <-vm.S:
waitTime.Stop()
return
}
}
}()
}

478
gctscript/vm/vm_test.go Normal file
View File

@@ -0,0 +1,478 @@
package vm
import (
"errors"
"os"
"path/filepath"
"reflect"
"testing"
"time"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/logger"
)
const (
maxTestVirtualMachines uint8 = 30
testVirtualMachineTimeout = time.Minute
scriptName = "1D01TH0RS3.gct"
)
var (
testScript = filepath.Join("..", "..", "testdata", "gctscript", "once.gct")
testInvalidScript = filepath.Join("..", "..", "testdata", "gctscript", "invalid.gct")
testBrokenScript = filepath.Join("..", "..", "testdata", "gctscript", "broken.gct")
testScriptRunner = filepath.Join("..", "..", "testdata", "gctscript", "timer.gct")
testScriptRunner1s = filepath.Join("..", "..", "testdata", "gctscript", "1s_timer.gct")
testScriptRunnerInvalid = filepath.Join("..", "..", "testdata", "gctscript", "invalid_timer.gct")
testScriptRunnerNegative = filepath.Join("..", "..", "testdata", "gctscript", "negative_timer.gct")
)
func TestMain(m *testing.M) {
c := logger.GenDefaultSettings()
c.Enabled = convert.BoolPtr(false)
logger.GlobalLogConfig = &c
GCTScriptConfig = configHelper(true, true, maxTestVirtualMachines)
os.Exit(m.Run())
}
func TestNewVM(t *testing.T) {
x := New()
xType := reflect.TypeOf(x).String()
if xType != "*vm.VM" {
t.Fatalf("vm.New should return pointer to VM instead received: %v", x)
}
}
func TestVMLoad(t *testing.T) {
GCTScriptConfig = configHelper(true, true, maxTestVirtualMachines)
testVM := New()
err := testVM.Load(testScript)
if err != nil {
t.Fatal(err)
}
testScript = testScript[0 : len(testScript)-4]
testVM = New()
err = testVM.Load(testScript)
if err != nil {
t.Fatal(err)
}
GCTScriptConfig = configHelper(false, false, maxTestVirtualMachines)
err = testVM.Load(testScript)
if err != nil {
if !errors.Is(err, ErrScriptingDisabled) {
t.Fatal(err)
}
}
GCTScriptConfig = configHelper(true, true, maxTestVirtualMachines)
}
func TestVMLoad1s(t *testing.T) {
testVM := New()
err := testVM.Load(testScriptRunner1s)
if err != nil {
t.Fatal(err)
}
testVM.CompileAndRun()
time.Sleep(5000)
err = testVM.Shutdown()
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
}
func TestVMLoadNegativeTimer(t *testing.T) {
testVM := New()
err := testVM.Load(testScriptRunnerNegative)
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
testVM.CompileAndRun()
err = testVM.Shutdown()
if err == nil {
t.Fatal("expect error on shutdown due to invalid VM")
}
}
func TestVMLoadNilVM(t *testing.T) {
testVM := New()
err := testVM.Load(testScript)
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
testVM = nil
err = testVM.Load(testScript)
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
}
func TestCompileAndRunNilVM(t *testing.T) {
vmcount := VMSCount.Len()
testVM := New()
err := testVM.Load(testScript)
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
err = testVM.Load(testScript)
if err != nil {
if !errors.Is(err, ErrNoVMLoaded) {
t.Fatal(err)
}
}
testVM = nil
testVM.CompileAndRun()
err = testVM.Shutdown()
if err == nil {
t.Fatal("VM should not be running with invalid timer")
}
if VMSCount.Len() == vmcount-1 {
t.Fatal("expected VM count to decrease")
}
}
func TestVMLoadNoFile(t *testing.T) {
testVM := New()
err := testVM.Load("missing file")
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
t.Fatal(err)
}
}
}
func TestVMCompile(t *testing.T) {
testVM := New()
err := testVM.Load(testScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err != nil {
t.Fatal(err)
}
}
func TestVMRun(t *testing.T) {
testVM := NewVM()
err := testVM.Load(testScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err != nil {
t.Fatal(err)
}
err = testVM.Run()
if err != nil {
t.Fatal(err)
}
}
func TestVMRunTX(t *testing.T) {
testVM := NewVM()
err := testVM.Load(testScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err != nil {
t.Fatal(err)
}
err = testVM.RunCtx()
if err != nil {
t.Fatal(err)
}
}
func TestVMWithRunner(t *testing.T) {
vmCount := VMSCount.Len()
VM := New()
if VM == nil {
t.Fatal("Failed to allocate new VM exiting")
}
err := VM.Load(testScriptRunner)
if err != nil {
t.Fatal(err)
}
if VMSCount.Len() == vmCount {
t.Fatal("expected VM count to increase")
}
VM.CompileAndRun()
err = VM.Shutdown()
if err != nil {
t.Fatal(err)
}
if VMSCount.Len() == vmCount-1 {
t.Fatal("expected VM count to decrease")
}
}
func TestVMWithRunnerOnce(t *testing.T) {
vmCount := VMSCount.Len()
VM := New()
if VM == nil {
t.Fatal("Failed to allocate new VM exiting")
}
err := VM.Load(testScript)
if err != nil {
t.Fatal(err)
}
if VMSCount.Len() == vmCount {
t.Fatal("expected VM count to increase")
}
VM.CompileAndRun()
err = VM.Shutdown()
if err == nil {
t.Fatal("VM should not be running with invalid timer")
}
}
func TestVMWithRunnerNegativeTimer(t *testing.T) {
vmCount := VMSCount.Len()
VM := New()
if VM == nil {
t.Fatal("Failed to allocate new VM exiting")
}
err := VM.Load(testScriptRunnerNegative)
if err != nil {
t.Fatal(err)
}
if VMSCount.Len() == vmCount {
t.Fatal("expected VM count to increase")
}
VM.CompileAndRun()
err = VM.Shutdown()
if err == nil {
t.Fatal("VM should not be running with invalid timer")
}
if VMSCount.Len() == vmCount-1 {
t.Fatal("expected VM count to decrease")
}
}
func TestShutdownAll(t *testing.T) {
vmCount := VMSCount.Len()
VM := New()
err := VM.Load(testScriptRunner)
if err != nil {
t.Fatal(err)
}
VM.CompileAndRun()
if VMSCount.Len() == vmCount {
t.Fatal("expected VM count to increase")
}
err = ShutdownAll()
if err != nil {
t.Fatal(err)
}
if VMSCount.Len() == vmCount-1 {
t.Fatal("expected VM count to decrease")
}
}
func TestRead(t *testing.T) {
VM := NewVM()
err := VM.Load(testScriptRunner)
if err != nil {
t.Fatal(err)
}
ScriptPath = filepath.Join("..", "..", "testdata", "gctscript")
data, err := VM.Read()
if err != nil {
t.Fatal(err)
}
if len(data) < 1 {
t.Fatal("expected data to be returned")
}
_ = VM.Shutdown()
}
func TestRemoveVM(t *testing.T) {
id, _ := uuid.FromString("6f20c907-64a0-48f2-848a-7837dee61672")
err := RemoveVM(id)
if err != nil {
if err.Error() != "VM 6f20c907-64a0-48f2-848a-7837dee61672 not found" {
t.Fatal(err)
}
}
}
func TestError_Error(t *testing.T) {
x := Error{
Script: "noscript.gct",
Action: "test",
Cause: errors.New("HELLO ERROR"),
}
if x.Error() != "GCT Script: (ACTION) test (SCRIPT) noscript.gct HELLO ERROR" {
t.Fatal(x.Error())
}
}
func TestVM_CompileInvalid(t *testing.T) {
testVM := New()
err := testVM.Load(testInvalidScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err != nil {
t.Fatal(err)
}
err = testVM.Run()
if err == nil {
t.Fatal("unexpected result broken script compiled successfully ")
}
testVM = New()
err = testVM.Load(testInvalidScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err != nil {
t.Fatal(err)
}
err = testVM.RunCtx()
if err == nil {
t.Fatal("unexpected result broken script compiled successfully ")
}
testVM = New()
err = testVM.Load(testInvalidScript)
if err != nil {
t.Fatal(err)
}
testVM.CompileAndRun()
err = testVM.Shutdown()
if err == nil {
t.Fatal("Shutdown() passed successfully but expected to fail with invalid script")
}
}
func TestVM_CompileBroken(t *testing.T) {
testVM := New()
err := testVM.Load(testBrokenScript)
if err != nil {
t.Fatal(err)
}
err = testVM.Compile()
if err == nil {
t.Fatal("unexpected result broken script compiled successfully ")
}
}
func TestVM_CompileAndRunBroken(t *testing.T) {
testVM := New()
err := testVM.Load(testBrokenScript)
if err != nil {
t.Fatal(err)
}
testVM.CompileAndRun()
err = testVM.Shutdown()
if err == nil {
t.Fatal("expect error on shutdown due to invalid VM")
}
}
func TestValidate(t *testing.T) {
err := Validate(testBrokenScript)
if err == nil {
t.Fatal(err)
}
err = Validate(testScript)
if err != nil {
t.Fatal(err)
}
}
func TestVMLimit(t *testing.T) {
GCTScriptConfig = configHelper(true, false, 0)
testVM := New()
if testVM != nil {
t.Fatal("expected nil but received pointer to VM")
}
GCTScriptConfig = configHelper(true, true, maxTestVirtualMachines)
}
func TestAutoload(t *testing.T) {
GCTScriptConfig = &Config{
Enabled: true,
AutoLoad: []string{
scriptName,
},
Verbose: true,
}
ScriptPath = filepath.Join("..", "..", "testdata", "gctscript")
err := Autoload(scriptName, true)
if err != nil {
t.Fatal(err)
}
err = Autoload(scriptName, true)
if err == nil {
t.Fatal("expected err to be script not found received nil")
}
err = Autoload("once", false)
if err != nil {
t.Fatal(err)
}
err = Autoload(scriptName, false)
if err == nil {
t.Fatal("expected err to be script not found received nil")
}
}
func TestVMCount(t *testing.T) {
var c vmscount
c.add()
if c.Len() != 1 {
t.Fatalf("expect c len to be 1 instead received %v", c.Len())
}
c.remove()
if c.Len() != 0 {
t.Fatalf("expect c len to be 0 instead received %v", c.Len())
}
}
func configHelper(enabled, imports bool, max uint8) *Config {
return &Config{
Enabled: enabled,
AllowImports: imports,
ScriptTimeout: testVirtualMachineTimeout,
MaxVirtualMachines: max,
Verbose: true,
}
}

61
gctscript/vm/vm_types.go Normal file
View File

@@ -0,0 +1,61 @@
package vm
import (
"context"
"sync"
"time"
"github.com/d5/tengo/v2"
"github.com/gofrs/uuid"
)
const (
// DefaultTimeoutValue default timeout value for virtual machines
DefaultTimeoutValue = 30 * time.Second
// DefaultMaxVirtualMachines max number of virtual machines that can be loaded at one time
DefaultMaxVirtualMachines uint8 = 10
// TypeLoad text to display in script_event table when a VM is loaded
TypeLoad = "load"
// TypeCreate text to display in script_event table when a VM is created
TypeCreate = "create"
// TypeExecute text to display in script_event table when a script is executed
TypeExecute = "execute"
// TypeStop text to display in script_event table when a running script is stopped
TypeStop = "stop"
// TypeRead text to display in script_event table when a script contents is read
TypeRead = "read"
// StatusSuccess text to display in script_event table on successful execution
StatusSuccess = "success"
// StatusFailure text to display in script_event table when script execution fails
StatusFailure = "failure"
)
type vmscount int32
var (
pool = &sync.Pool{
New: func() interface{} {
return new(tengo.Script)
},
}
// AllVMSync stores all current Virtual Machine instances
AllVMSync = &sync.Map{}
// VMSCount running total count of Virtual Machines
VMSCount vmscount
)
// VM contains a pointer to "script" (precompiled source) and "compiled" (compiled byte code) instances
type VM struct {
ID uuid.UUID
Hash string
File string
Path string
Script *tengo.Script
Compiled *tengo.Compiled
ctx context.Context
T time.Duration
NextRun time.Time
S chan struct{}
}

View File

@@ -0,0 +1,216 @@
package exchange
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/engine"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
)
// Exchange implements all required methods for Wrapper
type Exchange struct{}
// Exchanges returns slice of all current exchanges
func (e Exchange) Exchanges(enabledOnly bool) []string {
return engine.GetExchanges(enabledOnly)
}
// GetExchange returns IBotExchange for exchange or error if exchange is not found
func (e Exchange) GetExchange(exch string) (exchange.IBotExchange, error) {
ex := engine.GetExchangeByName(exch)
if ex == nil {
return nil, fmt.Errorf("%v exchange not found", exch)
}
return ex, nil
}
// IsEnabled returns if requested exchange is enabled or disabled
func (e Exchange) IsEnabled(exch string) bool {
ex, err := e.GetExchange(exch)
if err != nil {
return false
}
return ex.IsEnabled()
}
// Orderbook returns current orderbook requested exchange, pair and asset
func (e Exchange) Orderbook(exch string, pair currency.Pair, item asset.Item) (*orderbook.Base, error) {
return engine.GetSpecificOrderbook(pair, exch, item)
}
// Ticker returns ticker for provided currency pair & asset type
func (e Exchange) Ticker(exch string, pair currency.Pair, item asset.Item) (*ticker.Price, error) {
ex, err := e.GetExchange(exch)
if err != nil {
return nil, err
}
return ex.FetchTicker(pair, item)
}
// Pairs returns either all or enabled currency pairs
func (e Exchange) Pairs(exch string, enabledOnly bool, item asset.Item) (*currency.Pairs, error) {
x, err := engine.Bot.Config.GetExchangeConfig(exch)
if err != nil {
return nil, err
}
if enabledOnly {
return &x.CurrencyPairs.Get(item).Enabled, nil
}
return &x.CurrencyPairs.Get(item).Available, nil
}
// QueryOrder returns details of a valid exchange order
func (e Exchange) QueryOrder(exch, orderID string) (*order.Detail, error) {
ex, err := e.GetExchange(exch)
if err != nil {
return nil, err
}
r, err := ex.GetOrderInfo(orderID)
if err != nil {
return nil, err
}
return &r, nil
}
// SubmitOrder submit new order on exchange
func (e Exchange) SubmitOrder(exch string, submit *order.Submit) (*order.SubmitResponse, error) {
r, err := engine.Bot.OrderManager.Submit(exch, submit)
if err != nil {
return nil, err
}
return &r.SubmitResponse, nil
}
// CancelOrder wrapper to cancel order on exchange
func (e Exchange) CancelOrder(exch, orderID string) (bool, error) {
orderDetails, err := e.QueryOrder(exch, orderID)
if err != nil {
return false, err
}
cancel := &order.Cancel{
AccountID: orderDetails.AccountID,
OrderID: orderDetails.ID,
CurrencyPair: orderDetails.CurrencyPair,
Side: orderDetails.OrderSide,
}
err = engine.Bot.OrderManager.Cancel(exch, cancel)
if err != nil {
return false, err
}
return true, nil
}
// AccountInformation returns account information (balance etc) for requested exchange
func (e Exchange) AccountInformation(exch string) (*modules.AccountInfo, error) {
ex, err := e.GetExchange(exch)
if err != nil {
return nil, err
}
r, err := ex.GetAccountInfo()
if err != nil {
return nil, err
}
temp, err := json.Marshal(r)
if err != nil {
return nil, err
}
accountInfo := modules.AccountInfo{}
err = json.Unmarshal(temp, &accountInfo)
if err != nil {
return nil, err
}
return &accountInfo, nil
}
// DepositAddress gets the address required to deposit funds for currency type
func (e Exchange) DepositAddress(exch string, currencyCode currency.Code) (out string, err error) {
if currencyCode.IsEmpty() {
err = errors.New("currency code is empty")
return
}
return engine.Bot.DepositAddressManager.GetDepositAddressByExchange(exch, currencyCode)
}
// WithdrawalFiatFunds withdraw funds from exchange to requested fiat source
func (e Exchange) WithdrawalFiatFunds(exch, bankaccountid string, request *withdraw.FiatRequest) (string, error) {
ex, err := e.GetExchange(exch)
if err != nil {
return "", err
}
v, err := engine.Bot.Config.GetBankAccountByID(bankaccountid)
if err != nil {
return "", err
}
otp, err := engine.GetExchangeoOTPByName(exch)
if err == nil {
otpValue, errParse := strconv.ParseInt(otp, 10, 64)
if errParse != nil {
return "", errors.New("failed to generate OTP unable to continue")
}
request.GenericInfo.OneTimePassword = otpValue
}
request.BankAccountName = v.AccountName
request.BankAccountNumber = v.AccountNumber
request.BankName = v.BankName
request.BankAddress = v.BankAddress
request.BankCity = v.BankPostalCity
request.BankCountry = v.BankCountry
request.BankPostalCode = v.BankPostalCode
request.BSB = v.BSBNumber
request.SwiftCode = v.SWIFTCode
request.IBAN = v.IBAN
err = withdraw.Valid(request)
if err != nil {
return "", err
}
return ex.WithdrawFiatFunds(request)
}
// WithdrawalCryptoFunds withdraw funds from exchange to requested Crypto source
func (e Exchange) WithdrawalCryptoFunds(exch string, request *withdraw.CryptoRequest) (out string, err error) {
ex, err := e.GetExchange(exch)
if err != nil {
return "", err
}
otp, err := engine.GetExchangeoOTPByName(exch)
if err == nil {
v, errParse := strconv.ParseInt(otp, 10, 64)
if errParse != nil {
return "", errors.New("failed to generate OTP unable to continue")
}
request.GenericInfo.OneTimePassword = v
}
err = withdraw.Valid(request)
if err != nil {
return "", err
}
return ex.WithdrawCryptocurrencyFunds(request)
}

View File

@@ -0,0 +1,190 @@
package exchange
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/engine"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// change these if you wish to test another exchange and/or currency pair
const (
exchName = "BTC Markets" // change to test on another exchange
exchAPIKEY = ""
exchAPISECRET = ""
exchClientID = ""
pairs = "BTC-AUD" // change to test another currency pair
delimiter = "-"
assetType = asset.Spot
orderID = "1234"
orderType = order.Limit
orderSide = order.Buy
orderClientID = ""
orderPrice = 1
orderAmount = 1
)
var (
settings = engine.Settings{
ConfigFile: filepath.Join("..", "..", "..", "..", "testdata", "configtest.json"),
EnableDryRun: true,
DataDir: filepath.Join("..", "..", "..", "..", "testdata", "gocryptotrader"),
Verbose: false,
EnableGRPC: false,
EnableDeprecatedRPC: false,
EnableWebsocketRPC: false,
}
exchangeTest = Exchange{}
)
func TestMain(m *testing.M) {
var t int
err := setupEngine()
if err != nil {
fmt.Printf("Failed to configure exchange test cannot continue: %v", err)
os.Exit(1)
}
t = m.Run()
cleanup()
os.Exit(t)
}
func TestExchange_Exchanges(t *testing.T) {
t.Parallel()
x := exchangeTest.Exchanges(false)
y := len(x)
if y != 27 {
t.Fatalf("expected 27 received %v", y)
}
}
func TestExchange_GetExchange(t *testing.T) {
t.Parallel()
_, err := exchangeTest.GetExchange(exchName)
if err != nil {
t.Fatal(err)
}
_, err = exchangeTest.GetExchange("hello world")
if err == nil {
t.Fatal("unexpected error message received nil")
}
}
func TestExchange_IsEnabled(t *testing.T) {
t.Parallel()
x := exchangeTest.IsEnabled(exchName)
if !x {
t.Fatal("expected return to be true")
}
x = exchangeTest.IsEnabled("fake_exchange")
if x {
t.Fatal("expected return to be false")
}
}
func TestExchange_Ticker(t *testing.T) {
t.Parallel()
c := currency.NewPairDelimiter(pairs, delimiter)
_, err := exchangeTest.Ticker(exchName, c, assetType)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_Orderbook(t *testing.T) {
t.Parallel()
c := currency.NewPairDelimiter(pairs, delimiter)
_, err := exchangeTest.Orderbook(exchName, c, assetType)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_Pairs(t *testing.T) {
t.Parallel()
_, err := exchangeTest.Pairs(exchName, false, assetType)
if err != nil {
t.Fatal(err)
}
_, err = exchangeTest.Pairs(exchName, true, assetType)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_AccountInformation(t *testing.T) {
if !configureExchangeKeys() {
t.Skip("no exchange configured test skipped")
}
_, err := exchangeTest.AccountInformation(exchName)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_QueryOrder(t *testing.T) {
if !configureExchangeKeys() {
t.Skip("no exchange configured test skipped")
}
_, err := exchangeTest.QueryOrder(exchName, orderID)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_SubmitOrder(t *testing.T) {
if !configureExchangeKeys() {
t.Skip("no exchange configured test skipped")
}
tempOrder := &order.Submit{
Pair: currency.NewPairDelimiter(pairs, delimiter),
OrderType: orderType,
OrderSide: orderSide,
TriggerPrice: 0,
TargetAmount: 0,
Price: orderPrice,
Amount: orderAmount,
ClientID: orderClientID,
}
_, err := exchangeTest.SubmitOrder(exchName, tempOrder)
if err != nil {
t.Fatal(err)
}
}
func TestExchange_CancelOrder(t *testing.T) {
if !configureExchangeKeys() {
t.Skip("no exchange configured test skipped")
}
_, err := exchangeTest.CancelOrder(exchName, orderID)
if err != nil {
t.Fatal(err)
}
}
func setupEngine() (err error) {
engine.Bot, err = engine.NewFromSettings(&settings)
if err != nil {
return err
}
return engine.Bot.Start()
}
func cleanup() {
err := os.RemoveAll(settings.DataDir)
if err != nil {
fmt.Printf("Clean up failed to remove file: %v manual removal may be required", err)
}
}
func configureExchangeKeys() bool {
ex := engine.GetExchangeByName(exchName).GetBase()
ex.SetAPIKeys(exchAPIKEY, exchAPISECRET, exchClientID)
ex.SkipAuthCheck = true
return ex.ValidateAPICredentials()
}

View File

@@ -0,0 +1,10 @@
package gct
import "github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/gct/exchange"
// Setup returns a Wrapper
func Setup() *Wrapper {
return &Wrapper{
&exchange.Exchange{},
}
}

View File

@@ -0,0 +1,14 @@
package gct
import (
"reflect"
"testing"
)
func TestSetup(t *testing.T) {
x := Setup()
xType := reflect.TypeOf(x).String()
if xType != "*gct.Wrapper" {
t.Fatalf("Setup() should return pointer to Wrapper instead received: %v", x)
}
}

View File

@@ -0,0 +1,8 @@
package gct
import "github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/gct/exchange"
// Wrapper struct
type Wrapper struct {
*exchange.Exchange
}

View File

@@ -0,0 +1,211 @@
package validator
import (
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
)
// Exchanges validator for test execution/scripts
func (w Wrapper) Exchanges(enabledOnly bool) []string {
if enabledOnly {
return []string{
"hello world",
}
}
return []string{
"nope",
}
}
// IsEnabled returns if requested exchange is enabled or disabled
func (w Wrapper) IsEnabled(exch string) (v bool) {
if exch == exchError.String() {
return
}
return true
}
// Orderbook validator for test execution/scripts
func (w Wrapper) Orderbook(exch string, pair currency.Pair, item asset.Item) (*orderbook.Base, error) {
if exch == exchError.String() {
return nil, errTestFailed
}
return &orderbook.Base{
ExchangeName: exch,
AssetType: item,
Pair: pair,
Bids: []orderbook.Item{
{
Amount: 1,
Price: 1,
},
},
Asks: []orderbook.Item{
{
Amount: 1,
Price: 1,
},
},
}, nil
}
// Ticker validator for test execution/scripts
func (w Wrapper) Ticker(exch string, pair currency.Pair, item asset.Item) (*ticker.Price, error) {
if exch == exchError.String() {
return nil, errTestFailed
}
return &ticker.Price{
Last: 1,
High: 2,
Low: 3,
Bid: 4,
Ask: 5,
Volume: 6,
QuoteVolume: 7,
PriceATH: 8,
Open: 9,
Close: 10,
Pair: pair,
ExchangeName: exch,
AssetType: item,
LastUpdated: time.Now(),
}, nil
}
// Pairs validator for test execution/scripts
func (w Wrapper) Pairs(exch string, _ bool, _ asset.Item) (*currency.Pairs, error) {
if exch == exchError.String() {
return nil, errTestFailed
}
pairs := currency.NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"})
return &pairs, nil
}
// QueryOrder validator for test execution/scripts
func (w Wrapper) QueryOrder(exch, _ string) (*order.Detail, error) {
if exch == exchError.String() {
return nil, errTestFailed
}
return &order.Detail{
Exchange: exch,
AccountID: "hello",
ID: "1",
CurrencyPair: currency.NewPairFromString("BTCAUD"),
OrderSide: "ask",
OrderType: "limit",
OrderDate: time.Now(),
Status: "cancelled",
Price: 1,
Amount: 2,
ExecutedAmount: 1,
RemainingAmount: 0,
Fee: 0,
Trades: []order.TradeHistory{
{
Timestamp: time.Now(),
TID: "",
Price: 1,
Amount: 2,
Exchange: exch,
Type: "limit",
Side: "ask",
Fee: 0,
Description: "",
},
},
}, nil
}
// SubmitOrder validator for test execution/scripts
func (w Wrapper) SubmitOrder(exch string, _ *order.Submit) (*order.SubmitResponse, error) {
if exch == exchError.String() {
return nil, errTestFailed
}
tempOrder := &order.SubmitResponse{
IsOrderPlaced: false,
OrderID: exch,
}
if exch == "true" {
tempOrder.IsOrderPlaced = true
}
return tempOrder, nil
}
// CancelOrder validator for test execution/scripts
func (w Wrapper) CancelOrder(exch, orderid string) (bool, error) {
if exch == exchError.String() {
return false, errTestFailed
}
return orderid != "false", nil
}
// AccountInformation validator for test execution/scripts
func (w Wrapper) AccountInformation(exch string) (*modules.AccountInfo, error) {
if exch == exchError.String() {
return &modules.AccountInfo{}, errTestFailed
}
return &modules.AccountInfo{
Exchange: exch,
Accounts: []modules.Account{
{
ID: exch,
Currencies: []modules.AccountCurrencyInfo{
{
CurrencyName: currency.Code{
Item: &currency.Item{
ID: 0,
FullName: "Bitcoin",
Symbol: "BTC",
Role: 1,
AssocChain: "",
AssocExchange: nil,
},
},
TotalValue: 100,
Hold: 0,
},
},
},
},
}, nil
}
// DepositAddress validator for test execution/scripts
func (w Wrapper) DepositAddress(exch string, _ currency.Code) (string, error) {
if exch == exchError.String() {
return exch, errTestFailed
}
return exch, nil
}
// WithdrawalCryptoFunds validator for test execution/scripts
func (w Wrapper) WithdrawalCryptoFunds(exch string, _ *withdraw.CryptoRequest) (out string, err error) {
if exch == exchError.String() {
return exch, errTestFailed
}
return "", nil
}
// WithdrawalFiatFunds validator for test execution/scripts
func (w Wrapper) WithdrawalFiatFunds(exch, _ string, _ *withdraw.FiatRequest) (out string, err error) {
if exch == exchError.String() {
return exch, errTestFailed
}
return "123", nil
}

View File

@@ -0,0 +1,205 @@
package validator
import (
"testing"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
const (
exchName = "BTC Markets" // change to test on another exchange
exchAPIKEY = ""
exchAPISECRET = ""
exchClientID = ""
pairs = "BTC-AUD" // change to test another currency pair
delimiter = "-"
assetType = asset.Spot
orderID = "1234"
orderType = order.Limit
orderSide = order.Buy
orderClientID = ""
orderPrice = 1
orderAmount = 1
)
var (
currencyPair = currency.NewPairFromString("BTCAUD")
testWrapper = Wrapper{}
)
func TestWrapper_Exchanges(t *testing.T) {
t.Parallel()
x := testWrapper.Exchanges(false)
y := len(x)
if y != 1 {
t.Fatalf("expected 1 received %v", y)
}
x = testWrapper.Exchanges(true)
y = len(x)
if y != 1 {
t.Fatalf("expected 1 received %v", y)
}
}
func TestWrapper_IsEnabled(t *testing.T) {
t.Parallel()
f := testWrapper.IsEnabled("hello")
if !f {
t.Fatal("expected IsEnabled to return true for enabled exchange")
}
f = testWrapper.IsEnabled(exchError.String())
if f {
t.Fatal("expected IsEnabled to return false for disabled exchange")
}
}
func TestWrapper_AccountInformation(t *testing.T) {
t.Parallel()
_, err := testWrapper.AccountInformation(exchName)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.AccountInformation(exchError.String())
if err == nil {
t.Fatal("expected AccountInformation to return error on invalid name")
}
}
func TestWrapper_CancelOrder(t *testing.T) {
t.Parallel()
_, err := testWrapper.CancelOrder(exchName, orderID)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.CancelOrder(exchError.String(), "")
if err == nil {
t.Fatal("expected CancelOrder to return error on invalid name")
}
}
func TestWrapper_DepositAddress(t *testing.T) {
_, err := testWrapper.DepositAddress(exchError.String(), currency.NewCode("BTC"))
if err == nil {
t.Fatal("expected DepositAddress to return error on invalid name")
}
_, err = testWrapper.DepositAddress(exchName, currency.NewCode("BTC"))
if err != nil {
t.Fatal(err)
}
}
func TestWrapper_Orderbook(t *testing.T) {
t.Parallel()
c := currency.NewPairDelimiter(pairs, delimiter)
_, err := testWrapper.Orderbook(exchName, c, assetType)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.Orderbook(exchError.String(), currencyPair, asset.Spot)
if err == nil {
t.Fatal("expected Orderbook to return error with invalid name")
}
}
func TestWrapper_Pairs(t *testing.T) {
t.Parallel()
_, err := testWrapper.Pairs(exchName, false, assetType)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.Pairs(exchName, true, assetType)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.Pairs(exchError.String(), false, asset.Spot)
if err == nil {
t.Fatal("expected Pairs to return error on invalid name")
}
}
func TestWrapper_QueryOrder(t *testing.T) {
t.Parallel()
_, err := testWrapper.QueryOrder(exchName, orderID)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.QueryOrder(exchError.String(), "")
if err == nil {
t.Fatal("expected QueryOrder to return error on invalid name")
}
}
func TestWrapper_SubmitOrder(t *testing.T) {
t.Parallel()
tempOrder := &order.Submit{
Pair: currency.NewPairDelimiter(pairs, delimiter),
OrderType: orderType,
OrderSide: orderSide,
TriggerPrice: 0,
TargetAmount: 0,
Price: orderPrice,
Amount: orderAmount,
ClientID: orderClientID,
}
_, err := testWrapper.SubmitOrder("true", tempOrder)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.SubmitOrder(exchError.String(), nil)
if err == nil {
t.Fatal("expected SubmitOrder to return error with invalid name")
}
}
func TestWrapper_Ticker(t *testing.T) {
t.Parallel()
c := currency.NewPairDelimiter(pairs, delimiter)
_, err := testWrapper.Ticker(exchName, c, assetType)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.Ticker(exchError.String(), currencyPair, asset.Spot)
if err == nil {
t.Fatal("expected Ticker to return error with invalid name")
}
}
func TestWrapper_WithdrawalCryptoFunds(t *testing.T) {
_, err := testWrapper.WithdrawalCryptoFunds(exchError.String(), nil)
if err == nil {
t.Fatal("expected WithdrawalCryptoFunds to return error with invalid name")
}
_, err = testWrapper.WithdrawalCryptoFunds(exchName, nil)
if err != nil {
t.Fatal("expected WithdrawalCryptoFunds to return error with invalid name")
}
}
func TestWrapper_WithdrawalFiatFunds(t *testing.T) {
_, err := testWrapper.WithdrawalFiatFunds(exchError.String(), "", nil)
if err == nil {
t.Fatal("expected WithdrawalFiatFunds to return error with invalid name")
}
_, err = testWrapper.WithdrawalFiatFunds(exchName, "", nil)
if err != nil {
t.Fatal("expected WithdrawalCryptoFunds to return error with invalid name")
}
}

View File

@@ -0,0 +1,21 @@
package validator
import (
"errors"
"sync/atomic"
objects "github.com/d5/tengo/v2"
)
var (
// IsTestExecution if test is executed under test conditions
IsTestExecution atomic.Value
exchError = &objects.String{
Value: "error",
}
errTestFailed = errors.New("test failed")
)
// Wrapper for validator interface
type Wrapper struct{}

View File

@@ -0,0 +1,14 @@
package wrappers
import (
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
)
// GetWrapper returns the instance of each wrapper to use
func GetWrapper() modules.GCT {
if validator.IsTestExecution.Load() == true {
return validator.Wrapper{}
}
return modules.Wrapper
}

2
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/thrasher-corp/gocryptotrader
go 1.12
require (
github.com/d5/tengo/v2 v2.0.2
github.com/gofrs/uuid v3.2.0+incompatible
github.com/golang/protobuf v1.3.2
github.com/google/go-querystring v1.0.0
@@ -20,6 +21,7 @@ require (
github.com/thrasher-corp/sqlboiler v1.0.1-0.20191001234224-71e17f37a85e
github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb
github.com/urfave/cli v1.20.0
github.com/volatiletech/null v8.0.0+incompatible
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
golang.org/x/net v0.0.0-20190606173856-1492cefac77f // indirect
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c // indirect

2
go.sum
View File

@@ -25,6 +25,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I=
github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

View File

@@ -6,6 +6,8 @@ import (
"io/ioutil"
"os"
"strings"
"github.com/thrasher-corp/gocryptotrader/common/convert"
)
func getWriters(s *SubLoggerConfig) io.Writer {
@@ -32,23 +34,20 @@ func getWriters(s *SubLoggerConfig) io.Writer {
// GenDefaultSettings return struct with known sane/working logger settings
func GenDefaultSettings() (log Config) {
t := func(t bool) *bool { return &t }(true)
f := func(f bool) *bool { return &f }(false)
log = Config{
Enabled: t,
Enabled: convert.BoolPtr(true),
SubLoggerConfig: SubLoggerConfig{
Level: "INFO|DEBUG|WARN|ERROR",
Output: "console",
},
LoggerFileConfig: &loggerFileConfig{
FileName: "log.txt",
Rotate: f,
Rotate: convert.BoolPtr(false),
MaxSize: 0,
},
AdvancedSettings: advancedSettings{
ShowLogSystemName: f,
Spacer: " | ",
ShowLogSystemName: convert.BoolPtr(false),
Spacer: spacer,
TimeStampFormat: timestampFormat,
Headers: headers{
Info: "[INFO]",
@@ -145,6 +144,7 @@ func init() {
PortfolioMgr = registerNewSubLogger("PORTFOLIO")
SyncMgr = registerNewSubLogger("SYNC")
TimeMgr = registerNewSubLogger("TIMEKEEPER")
GCTScriptMgr = registerNewSubLogger("GCTSCRIPT")
WebsocketMgr = registerNewSubLogger("WEBSOCKET")
EventMgr = registerNewSubLogger("EVENT")
DispatchMgr = registerNewSubLogger("DISPATCH")

View File

@@ -7,6 +7,9 @@ import (
const (
timestampFormat = " 02/01/2006 15:04:05 "
spacer = " | "
// DefaultMaxFileSize for logger rotation file
DefaultMaxFileSize int64 = 100
)
var (

View File

@@ -7,7 +7,7 @@ import (
// Info takes a pointer subLogger struct and string sends to newLogEvent
func Info(sl *subLogger, data string) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -20,7 +20,7 @@ func Info(sl *subLogger, data string) {
// Infoln takes a pointer subLogger struct and interface sends to newLogEvent
func Infoln(sl *subLogger, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -33,7 +33,7 @@ func Infoln(sl *subLogger, v ...interface{}) {
// Infof takes a pointer subLogger struct, string & interface formats and sends to Info()
func Infof(sl *subLogger, data string, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -46,7 +46,7 @@ func Infof(sl *subLogger, data string, v ...interface{}) {
// Debug takes a pointer subLogger struct and string sends to multiwriter
func Debug(sl *subLogger, data string) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -59,7 +59,7 @@ func Debug(sl *subLogger, data string) {
// Debugln takes a pointer subLogger struct, string and interface sends to newLogEvent
func Debugln(sl *subLogger, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -72,7 +72,7 @@ func Debugln(sl *subLogger, v ...interface{}) {
// Debugf takes a pointer subLogger struct, string & interface formats and sends to Info()
func Debugf(sl *subLogger, data string, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -85,7 +85,7 @@ func Debugf(sl *subLogger, data string, v ...interface{}) {
// Warn takes a pointer subLogger struct & string and sends to newLogEvent()
func Warn(sl *subLogger, data string) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -98,7 +98,7 @@ func Warn(sl *subLogger, data string) {
// Warnln takes a pointer subLogger struct & interface formats and sends to newLogEvent()
func Warnln(sl *subLogger, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -111,7 +111,7 @@ func Warnln(sl *subLogger, v ...interface{}) {
// Warnf takes a pointer subLogger struct, string & interface formats and sends to Warn()
func Warnf(sl *subLogger, data string, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -124,7 +124,7 @@ func Warnf(sl *subLogger, data string, v ...interface{}) {
// Error takes a pointer subLogger struct & interface formats and sends to newLogEvent()
func Error(sl *subLogger, data ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -137,7 +137,7 @@ func Error(sl *subLogger, data ...interface{}) {
// Errorln takes a pointer subLogger struct, string & interface formats and sends to newLogEvent()
func Errorln(sl *subLogger, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -150,7 +150,7 @@ func Errorln(sl *subLogger, v ...interface{}) {
// Errorf takes a pointer subLogger struct, string & interface formats and sends to Debug()
func Errorf(sl *subLogger, data string, v ...interface{}) {
if sl == nil {
if sl == nil || !enabled() {
return
}
@@ -166,3 +166,10 @@ func displayError(err error) {
log.Printf("Logger write error: %v\n", err)
}
}
func enabled() bool {
if GlobalLogConfig.Enabled == nil {
return false
}
return *GlobalLogConfig.Enabled
}

View File

@@ -9,6 +9,7 @@ var (
CommunicationMgr *subLogger
ConfigMgr *subLogger
DatabaseMgr *subLogger
GCTScriptMgr *subLogger
OrderMgr *subLogger
PortfolioMgr *subLogger
SyncMgr *subLogger

View File

@@ -13,6 +13,8 @@ import (
"github.com/thrasher-corp/gocryptotrader/dispatch"
"github.com/thrasher-corp/gocryptotrader/engine"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/gctscript"
gctscriptVM "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
log "github.com/thrasher-corp/gocryptotrader/logger"
"github.com/thrasher-corp/gocryptotrader/signaler"
)
@@ -44,6 +46,7 @@ func main() {
flag.BoolVar(&settings.EnableDepositAddressManager, "depositaddressmanager", true, "enables the deposit address manager")
flag.BoolVar(&settings.EnableConnectivityMonitor, "connectivitymonitor", true, "enables the connectivity monitor")
flag.BoolVar(&settings.EnableDatabaseManager, "databasemanager", true, "enables database manager")
flag.BoolVar(&settings.EnableGCTScriptManager, "gctscriptmanager", true, "enables gctscript manager")
flag.DurationVar(&settings.EventManagerDelay, "eventmanagerdelay", time.Duration(0), "sets the event managers sleep delay between event checking")
flag.BoolVar(&settings.EnableNTPClient, "ntpclient", true, "enables the NTP client to check system clock drift")
flag.BoolVar(&settings.EnableDispatcher, "dispatch", true, "enables the dispatch system")
@@ -85,6 +88,9 @@ func main() {
flag.StringVar(&settings.GlobalHTTPUserAgent, "globalhttpuseragent", "", "sets the common HTTP client's user agent")
flag.StringVar(&settings.GlobalHTTPProxy, "globalhttpproxy", "", "sets the common HTTP client's proxy server")
// GCTScript tuning settings
flag.UintVar(&settings.MaxVirtualMachines, "maxvirtualmachines", uint(gctscriptVM.DefaultMaxVirtualMachines), "set max virtual machines that can load")
flag.Parse()
if *versionFlag {
@@ -103,6 +109,8 @@ func main() {
os.Exit(1)
}
gctscript.Setup()
engine.PrintSettings(&engine.Bot.Settings)
if err = engine.Bot.Start(); err != nil {
log.Errorf(log.Global, "Unable to start bot engine. Error: %s\n", err)

View File

@@ -18,7 +18,7 @@ func TestNTPClient(t *testing.T) {
t.Error("NTPClient should return valid time received zero value")
}
const timeFormat = "2006-01-02T15:04:05"
const timeFormat = "2006-01-02T15:04"
if v.UTC().Format(timeFormat) != time.Now().UTC().Format(timeFormat) {
t.Errorf("NTPClient returned incorrect time received: %v", v.UTC().Format(timeFormat))

11
testdata/gctscript/1s_timer.gct vendored Normal file
View File

@@ -0,0 +1,11 @@
fmt := import("fmt")
t := import("times")
name := "run"
timer := "1ns"
load := func() {
fmt.printf("1ns %s\n",t.now())
}
load()

BIN
testdata/gctscript/archived.zip vendored Normal file

Binary file not shown.

1
testdata/gctscript/broken.gct vendored Normal file
View File

@@ -0,0 +1 @@
hello

BIN
testdata/gctscript/broken.zip vendored Normal file

Binary file not shown.

3
testdata/gctscript/invalid.gct vendored Normal file
View File

@@ -0,0 +1,3 @@
fmt := import("fmt")
fmt.printnope("hello world")

3
testdata/gctscript/invalid_timer.gct vendored Normal file
View File

@@ -0,0 +1,3 @@
fmt := import("fmt")
timer := "abc1234"
fmt.print("hello")

11
testdata/gctscript/negative_timer.gct vendored Normal file
View File

@@ -0,0 +1,11 @@
fmt := import("fmt")
t := import("times")
name := "run"
timer := "-5s"
load := func() {
fmt.printf("5s %s\n",t.now())
}
load()

Some files were not shown because too many files have changed in this diff Show More