github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/cmd/natsql/flag.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/nats-io/nats.go"
    11  
    12  	"github.com/dop251/goja"
    13  
    14  	"github.com/angenalZZZ/gofunc/data"
    15  	"github.com/angenalZZZ/gofunc/f"
    16  	"github.com/angenalZZZ/gofunc/js"
    17  	"github.com/angenalZZZ/gofunc/log"
    18  	nat "github.com/angenalZZZ/gofunc/rpc/nats"
    19  )
    20  
    21  var (
    22  	//flagMsgLimit = flag.Int("c", 100000000, "the nats-Limits for pending messages for this subscription")
    23  	//flagBytesLimit = flag.Int("d", 4096, "the nats-Limits for a message's bytes for this subscription")
    24  	flagConfig = flag.String("c", "natsql.yaml", "sets config file")
    25  	flagTest   = flag.String("t", "", "sets json file and run SQL test")
    26  	flagAddr   = flag.String("a", "", "the NatS-Server address")
    27  	flagName   = flag.String("name", "", "the NatS-Subscription name prefix [required]")
    28  	flagToken  = flag.String("token", "", "the NatS-Token auth string [required]")
    29  	flagCred   = flag.String("cred", "", "the NatS-Cred file")
    30  	flagCert   = flag.String("cert", "", "the NatS-TLS cert file")
    31  	flagKey    = flag.String("key", "", "the NatS-TLS key file")
    32  )
    33  
    34  var (
    35  	isTest = false
    36  	// js test json data file
    37  	jsonFile string
    38  	// js runtime and register
    39  	jsr *js.GoRuntime
    40  )
    41  
    42  // Your Arguments.
    43  func initArgs() {
    44  	flag.Usage = func() {
    45  		fmt.Printf(" Usage of %s:\n", os.Args[0])
    46  		flag.PrintDefaults()
    47  	}
    48  	flag.Parse()
    49  }
    50  
    51  // Check Arguments And Init Config.
    52  func checkArgs() {
    53  	if *flagConfig != "" {
    54  		configFile = *flagConfig
    55  	}
    56  
    57  	if err := initConfig(); err != nil {
    58  		panic(err)
    59  	}
    60  
    61  	if *flagAddr != "" {
    62  		configInfo.Nats.Addr = *flagAddr
    63  	}
    64  	if *flagToken != "" {
    65  		configInfo.Nats.Token = *flagToken
    66  	}
    67  	if *flagCred != "" {
    68  		configInfo.Nats.Cred = *flagCred
    69  	}
    70  	if *flagCert != "" {
    71  		configInfo.Nats.Cert = *flagCert
    72  	}
    73  	if *flagKey != "" {
    74  		configInfo.Nats.Key = *flagKey
    75  	}
    76  
    77  	if *flagTest != "" {
    78  		jsonFile = *flagTest
    79  	}
    80  	if jsonFile != "" {
    81  		isTest = true
    82  	}
    83  	if isTest {
    84  		configInfo.Log.Level = "debug"
    85  	}
    86  
    87  	if log.Log == nil {
    88  		log.Log = log.Init(configInfo.Log)
    89  	}
    90  	if nat.Log == nil {
    91  		nat.Log = log.Log
    92  	}
    93  	js.RunLogTimeFormat = configInfo.Log.TimeFormat
    94  
    95  	// 全局订阅前缀:subject
    96  	if *flagName != "" {
    97  		subject = *flagName
    98  	}
    99  	if subject == "" {
   100  		subject = configInfo.Nats.Subscribe
   101  	}
   102  	if subject == "" {
   103  		panic("the subscription name prefix can't be empty.")
   104  	}
   105  
   106  	if cacheDir == "" {
   107  		if configInfo.Dir == "" {
   108  			cacheDir = filepath.Join(data.CurrentDir, subject)
   109  		} else {
   110  			cacheDir = filepath.Join(data.CurrentDir, configInfo.Dir)
   111  		}
   112  	}
   113  	if f.PathExists(cacheDir) == false {
   114  		panic("the cache disk directory is not found.")
   115  	}
   116  
   117  	if configInfo.Nats.Amount < 1 {
   118  		configInfo.Nats.Amount = -1
   119  	}
   120  	if configInfo.Nats.Bulk < 1 {
   121  		configInfo.Nats.Bulk = 1
   122  	}
   123  	if configInfo.Nats.Amount > 0 && configInfo.Nats.Amount < configInfo.Nats.Bulk {
   124  		configInfo.Nats.Amount = configInfo.Nats.Bulk
   125  	}
   126  	if configInfo.Nats.Interval < 1 {
   127  		configInfo.Nats.Interval = 1
   128  	}
   129  
   130  	nat.Log.Debug().Msgf("configuration complete")
   131  }
   132  
   133  // Create a subscriber for Client Connect.
   134  func natClientConnect(isGlobal bool, subj string) (conn *nats.Conn) {
   135  	var err error
   136  
   137  	// NatS
   138  	if isGlobal {
   139  		nat.Subject = subj
   140  		nat.Conn, err = nat.New(subj, configInfo.Nats.Addr, configInfo.Nats.Cred, configInfo.Nats.Token, configInfo.Nats.Cert, configInfo.Nats.Key)
   141  		if err != nil {
   142  			nat.Log.Error().Msgf("[nats] failed connect to server: %v\n", err)
   143  			os.Exit(1)
   144  		}
   145  		return nat.Conn
   146  	}
   147  
   148  	conn, err = nat.New(subj, configInfo.Nats.Addr, configInfo.Nats.Cred, configInfo.Nats.Token, configInfo.Nats.Cert, configInfo.Nats.Key)
   149  	if err != nil {
   150  		nat.Log.Error().Msgf("[nats] failed connect to server: %v\n", err)
   151  		os.Exit(1)
   152  	}
   153  	return
   154  }
   155  
   156  // Init Subscribers And Handlers.
   157  func createHandlers() {
   158  	if handlers == nil {
   159  		handlers = make([]*handler, 0)
   160  	}
   161  
   162  	stopHandlers() // Stop Subscribers And Handlers.
   163  
   164  	if jsr == nil {
   165  		p := js.GoRuntimeParam{
   166  			CacheDir: cacheDir,
   167  			DbType:   configInfo.Db.Type,
   168  			DbConn:   configInfo.Db.Conn,
   169  		}
   170  		_ = f.Mkdir(p.CacheDir)
   171  		jsr = js.NewRuntime(&p)
   172  	}
   173  	defer jsr.Clear()
   174  
   175  	if _, err := jsr.RunString(configInfo.Nats.Script); err != nil {
   176  		return
   177  	}
   178  	self := jsr.Runtime.Get("subscribe")
   179  	if self == nil {
   180  		return
   181  	}
   182  	objs, ok := self.Export().([]interface{})
   183  	if !ok {
   184  		return
   185  	}
   186  
   187  	dir1, js1 := cacheDir, configInfo.Js
   188  	if js1 == "" {
   189  		js1 = "natsql.js"
   190  	}
   191  
   192  	handlers = make([]*handler, 0)
   193  
   194  	for _, obj := range objs {
   195  		objMap, ok := obj.(map[string]interface{})
   196  		if !ok {
   197  			return
   198  		}
   199  
   200  		var itemName, itemSpec, itemSubj, itemDir string
   201  		if itemName, ok = objMap["name"].(string); !ok || itemName == "" {
   202  			return
   203  		}
   204  		if itemSpec, ok = objMap["spec"].(string); !ok {
   205  			return
   206  		}
   207  		if itemSpec == "+" {
   208  			itemSubj = subject + itemName
   209  		} else {
   210  			itemSubj = itemName
   211  		}
   212  		itemFunc, ok := objMap["func"].(func(goja.FunctionCall) goja.Value)
   213  		if !ok {
   214  			return
   215  		}
   216  		res := itemFunc(goja.FunctionCall{This: jsr.ToValue(obj)})
   217  		if res == nil || res.String() == "" {
   218  			itemDir = filepath.Join(dir1, itemName)
   219  		} else {
   220  			itemDir = filepath.Join(dir1, res.String())
   221  		}
   222  
   223  		conf := f.Clone(configInfo).(*Config)
   224  		conf.Dir, conf.Js = itemDir, filepath.Join(itemDir, js1)
   225  
   226  		h := new(handler)
   227  		h.jsFile = conf.Js
   228  		h.isScriptMod()
   229  		if err := h.doScriptMod(); err != nil {
   230  			return
   231  		}
   232  
   233  		// js global variable
   234  		jso := make(map[string]interface{})
   235  		jso["config"] = conf
   236  		h.jso = jso
   237  
   238  		// js runtime and register param
   239  		h.jsp = &js.GoRuntimeParam{
   240  			CacheDir:   filepath.Join(dir1, itemName),
   241  			DbType:     conf.Db.Type,
   242  			DbConn:     conf.Db.Conn,
   243  			NatSubject: itemSubj,
   244  		}
   245  
   246  		ctx, wait := f.ContextWithWait(context.TODO())
   247  
   248  		// natS subscriber
   249  		if !isTest {
   250  			// Create a subscriber for Client Connect
   251  			conn := natClientConnect(false, itemSubj)
   252  			h.jsp.NatConn = conn
   253  
   254  			sub := nat.NewSubscriberFastCache(conn, itemSubj, itemDir)
   255  			sub.Hand = h.Handle
   256  
   257  			h.Context, h.sub = ctx, sub
   258  		}
   259  
   260  		p, err := goja.Compile(itemSubj, h.js, false)
   261  		if err != nil {
   262  			return
   263  		}
   264  		vm := js.NewRuntime(h.jsp)
   265  		if _, err = vm.Runtime.RunProgram(p); err != nil {
   266  			return
   267  		}
   268  
   269  		h.jsr, h.jsName = vm, "sql"
   270  		if val := vm.Runtime.Get(h.jsName); val == nil {
   271  			h.jsName = "records"
   272  		} else {
   273  			if err := vm.Runtime.ExportTo(val, &h.jsFunc); err != nil {
   274  				h.jsName = "records"
   275  			}
   276  		}
   277  
   278  		// Run natS subscriber
   279  		if !isTest {
   280  			go h.sub.Run(wait)
   281  		}
   282  
   283  		handlers = append(handlers, h)
   284  	}
   285  }
   286  
   287  // Stop Subscribers And Handlers.
   288  func stopHandlers() {
   289  	for _, h := range handlers {
   290  		if h.sub != nil && h.sub.Running {
   291  			f.DoneContext(h.Context)
   292  			h.sub.Stop()
   293  			h.Stop()
   294  		}
   295  	}
   296  }
   297  
   298  // Init complete.
   299  func runInit() {
   300  	if isTest {
   301  		return
   302  	}
   303  
   304  	createHandlers() // Init Subscribers And Handlers.
   305  }
   306  
   307  // Run script test.
   308  func runTest() {
   309  	if !isTest {
   310  		return
   311  	}
   312  
   313  	createHandlers() // Init Subscribers And Handlers.
   314  
   315  	for _, h := range handlers {
   316  		itemDir := h.jso["configDir"].(string)
   317  		filename := filepath.Join(itemDir, jsonFile)
   318  		item, err := f.ReadFile(filename)
   319  		if err != nil {
   320  			panic(fmt.Errorf("test json file %q not opened: %s", jsonFile, err.Error()))
   321  		}
   322  
   323  		list, err := data.ListData(item)
   324  		if err != nil {
   325  			panic(err)
   326  		}
   327  		if len(list) == 0 {
   328  			panic(fmt.Errorf("test json file can't be empty"))
   329  		}
   330  
   331  		nat.Log.Debug().Msgf("test json file: %q %d records\r\n", jsonFile, len(list))
   332  
   333  		if subject == "" {
   334  			subject = "test"
   335  		}
   336  		if err = h.Handle(list); err != nil {
   337  			panic(err)
   338  		}
   339  	}
   340  
   341  	nat.Log.Debug().Msg("test finished.")
   342  	os.Exit(0)
   343  }