Categorygithub.com/genkai-net/genkai-server-go
modulepackage
1.2.1
Repository: https://github.com/genkai-net/genkai-server-go.git
Documentation: pkg.go.dev

# 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!"}

# Packages

No description provided by the author

# Functions

No description provided by the author

# Variables

No description provided by the author

# Structs

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Interfaces

No description provided by the author

# Type aliases

No description provided by the author