genkai

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2020 License: MIT Imports: 6 Imported by: 0

README

Genkai Go Server

Genkai is a dead simple function adapter between go and any language. It uses traditional HTTP for maximum adaptability with support for other transports coming soon.

Installation
$ go get github.com/genkai-net/genkai-server-go
Usage

Genkai as of now only supports plugging in to a GIN http server.


func main() {
    engine := gin.Default()
    
	kit := genkai.New()
	kit.Func("join", func(a, b string) string {
		return strings.Join(strs, ":")
	})

	kit.GinInstall(engine)
	panic(engine.Run("localhost:9302"))
}

Genkai installs itself alongside a GIN server, with a join function exposed.

To use on another language (ex. JS) using https://github.com/genkai-net/genkai-client-js

import {GenkaiClient, GenkaiClientJSON} from "genkai-client";

let app = GenkaiClient("http://localhost:9302");
let response = await app.join("hello", "world");
console.log(response);

Would result into an output of

hello:world
Contextual functions

The sample above is an example of an non-contextual function. Meaning the function has no way for differentiating who calls the function. Fortunately, Genkai comes with session tracking out of the box by prepending ctx *genkai.Context to the function's parameters.

	kit.Func("join", func(ctx *genkai.Context, a, b string) string {
            j := strings.Join(strs, ":")
            return fmt.Sprintf("$v's result: %v", ctx.Session, j)
	})

Running the JS snippet above would render

__genkai_endpointomo5k5llowcrsxq1kumvg's result: hello:world

The session ID persistence depends on the client library, in genkai-client-js the session ID is stored in SessionStorage. As such, it persists browser refreshes until the tab is closed.

Errors

Since most go functions comes with errors alongside the return value, Genkai supports these out of the box.

	kit.Func("login", func(ctx *genkai.Context, username, password string) (string, error) {
            if username != "myuser" && password != "secret" {
            	return "", fmt.Errorf("Invalid credentials")
            }
            
            return "account_secret_token", nil
	})

Here are some various possible errors that would arise from using this function.

let app = GenkaiClient("http://localhost:9302");
let token = await app.login("myuser", "wrong_secret");
>> Throws: Error: Invalid credentials

let token = await app.login("myuser");
>> Throws: Error: Function accepts 2 params <string, string>, provided 1

let token = await app.rogin("myuser", "secret");
>> Throws: Error: function 'rogin' does not exist

let token = await app.login("myuser", "secret");
        \-> "account_secret_token"
JSONMode

If your function parameters requires far a more complex input outside of the usual strings and numbers, Genkai comes with a struct binding support similar to GIN's ctx.BindJSON.

    type LoginPayload struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    kit.Func("login", func(ctx *genkai.Context) (string, error) {
        payload := LoginPayload{}
        err := ctx.BindJSON(&payload)
        if err != nil {
            return "", err	
        }

        if payload.username != "myuser" && payload.password != "secret" {
            return "", fmt.Errorf("Invalid credentials")
        }
        
        return "account_secret_token", nil
    })

Accessing this type of function requires GenkaiClientJSON instead of GenkaiClient, calling this function on the vanilla client usually throws the Error: Function accepts 0 params <*genkai.Context>, provided * error.

let app = GenkaiClientJSON("http://localhost:9302");
let token = await app.login({
    username: "myuser",
    password: "secret"
});
"account_secret_token"

Restrictions

  • A JSONMode function must only have *genkai.Context as it's only parameter.
  • You can pass any JS object as long as go's encoding/json is capable of unmarshalling it.
Structs

Genkai supports exposing struct methods out of the box same rules apply for the functions within the struct.


func main() {
	manager_instance := &Manager{
		Store:    map[string]string{},
		Sessions: map[string]string{},
		Pipe:     map[string]chan string{},
	}

	engine := gin.Default()
	c := cors.DefaultConfig()
	c.AllowAllOrigins = true
	c.AllowHeaders = append(c.AllowHeaders, "genkai-session")
	engine.Use(cors.New(c))

	kit := genkai.New()
	kit.Struct("man", manager_instance)
	kit.GinInstall(engine)

	panic(engine.Run("localhost:9302"))
}

type Manager struct {
	Store    map[string]string
	Sessions map[string]string
	Pipe     map[string]chan string
}

func (m *Manager) Register(username string) error {
	m.Store[username] = ""
	m.Pipe[username] = make(chan string, 5)
	return nil
}

func (m *Manager) Login(ctx *genkai.Context, username string) error {
	_, exists := m.Store[username]
	if !exists {
		return fmt.Errorf("user not found")
	}
	m.Sessions[ctx.Session] = username
	return nil
}

func (m *Manager) GetStore(ctx *genkai.Context) (string, error) {
	value, exists := m.Store[m.Sessions[ctx.Session]]
	if !exists {
		return "", fmt.Errorf("session not found")
	}
	return value, nil
}

func (m *Manager) SetStore(ctx *genkai.Context, value string) error {
	user, exists := m.Sessions[ctx.Session]
	if !exists {
		return fmt.Errorf("session not found")
	}

	_, exists = m.Store[user]
	if !exists {
		return fmt.Errorf("user not found")
	}

	m.Store[user] = value
	return nil
}

func (m *Manager) Details(ctx *genkai.Context) (gin.H, error) {
	user := m.Sessions[ctx.Session]
	value, exists := m.Store[user]
	if !exists {
		return nil, fmt.Errorf("session not found")
	}
	return gin.H{
		"session": ctx.Session,
		"user":    user,
		"store":   value,
	}, nil
}

func (m *Manager) Push(ctx *genkai.Context, value string) error {
	pipe, exists := m.Pipe[m.Sessions[ctx.Session]]
	if !exists {
		return fmt.Errorf("session not found")
	}
	pipe <- value
	return nil
}

func (m *Manager) Pop(ctx *genkai.Context) (string, error) {
	pipe, exists := m.Pipe[m.Sessions[ctx.Session]]
	if !exists {
		return "", fmt.Errorf("session not found")
	}

	return <-pipe, nil
}

JS usage is almost the same as calling a function but with a $ separator to distinguish the struct name and the function name. In this case, the struct is exposed as man due to the kit.Struct("man", manager_instance) declaration.

let app = GenkaiClient("http://localhost:9302");
await app.man$Register("noku"); // Call func (m *Manager) Register(string) error 
await app.man$Login("noku");
await app.man$GetStore();
 \-> ""
await app.man$SetStore("Hello world!")
await app.man$GetStore();
 \-> "Hello world!"
await app.man$Details();
 \-> {session: "__genkai_endpoint...", user: "noku", store: "Hello world!"}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var MissingJSONError = fmt.Errorf("The function is JSONMode but no JSON was supplied")

Functions

This section is empty.

Types

type Context

type Context struct {
	Session string
	Request *RequestPayload
	Ctx     context.Context
}

func (*Context) BindJSON

func (c *Context) BindJSON(v interface{}) error

type ContextFunc

type ContextFunc = func(ctx *Context) I

type I

type I = interface{}

type Kai

type Kai struct {
	// contains filtered or unexported fields
}

func New

func New() *Kai

func (*Kai) Execute

func (k *Kai) Execute(ctx *Context) (*ResultPayload, error)

func (*Kai) Func

func (k *Kai) Func(name string, c interface{})

func (*Kai) GinHandler

func (k *Kai) GinHandler(g *gin.Context)

func (*Kai) GinInstall

func (k *Kai) GinInstall(engine *gin.Engine, customPath ...string)

func (*Kai) Struct

func (k *Kai) Struct(name string, s interface{})

type RequestPayload

type RequestPayload struct {
	ID           string        `json:"id"`
	Parameters   []interface{} `json:"p"`
	FunctionName string        `json:"fn" binding:"required"`
	JSONData     string        `json:"json"`
}

type ResultPayload

type ResultPayload struct {
	ID      string      `json:"id,omitempty"`
	Returns interface{} `json:"r"`
	Error   interface{} `json:"e"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL