aicra

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2020 License: MIT Imports: 8 Imported by: 0

README

| aicra |

Go version Go Report Card Go doc Build Status

Aicra is a configuration-driven REST API engine written in Go.

Most of the management is done for you using a configuration file describing your API. you're left with implementing :

  • handlers
  • optionnally middle-wares (e.g. authentication, csrf)
  • and optionnally your custom type checkers to check input parameters

A example project is available here

Table of contents

I/ Installation

You need a recent machine with go installed. This package has not been tested under the version 1.14.

go get -u git.xdrm.io/go/aicra/cmd/aicra

The library should now be available as git.xdrm.io/go/aicra in your imports.

II/ Usage

1) Build a server

Here is some sample code that builds and sets up an aicra server using your api configuration file.

package main

import (
	"log"
	"net/http"
	"os"

	"git.xdrm.io/go/aicra"
	"git.xdrm.io/go/aicra/api"
	"git.xdrm.io/go/aicra/datatype/builtin"
)

func main() {

	builder := &aicra.Builder{}

	// add datatypes your api uses
	builder.AddType(builtin.BoolDataType{})
	builder.AddType(builtin.UintDataType{})
	builder.AddType(builtin.StringDataType{})

	config, err := os.Open("./api.json")
	if err != nil {
		log.Fatalf("cannot open config: %s", err)
	}

	// pass your configuration
	err = builder.Setup(config)
	config.Close()
	if err != nil {
		log.Fatalf("invalid config: %s", err)
	}

	// bind your handlers
	builder.Bind(http.MethodGet, "/user/{id}", getUserById)
	builder.Bind(http.MethodGet, "/user/{id}/username", getUsernameByID)

	// build the server and start listening
	server, err := builder.Build()
	if err != nil {
		log.Fatalf("cannot build server: %s", err)
	}
	http.ListenAndServe("localhost:8080", server)
}

Here is an example handler

type req struct{
	Param1 int
	Param3 *string // optional are pointers
}
type res struct{
	Output1 string
	Output2 bool
}

func myHandler(r req) (*res, api.Error) {
	err := doSomething()
	if err != nil {
		return nil, api.ErrorFailure
	}
	return &res{}, api.ErrorSuccess
}
2) API Configuration

The whole api behavior is described inside a json file (e.g. usually api.json). For a better understanding of the format, take a look at this working template. This file defines :

  • routes and their methods
  • every input for each method (called argument)
  • every output for each method
  • scope permissions (list of permissions needed by clients)
  • input policy :
    • type of argument (c.f. data types)
    • required/optional
    • variable renaming
Format

The root of the json file must be an array containing your requests definitions. For each, you will have to create fields described in the table above.

field path description example
info A short human-readable description of what the method does create a new user
scope A 2-dimensional array of permissions. The first dimension can be translated to a or operator, the second dimension as a and. It allows you to combine permissions in complex ways. [["A", "B"], ["C", "D"]] can be translated to : this method needs users to have permissions (A and B) or (C and D)
in The list of arguments that the clients will have to provide. Read more.
out The list of output data that will be returned by your controllers. It has the same syntax as the in field but optional parameters are not allowed
Input Arguments

Input arguments defines what data from the HTTP request the method needs. Aicra is able to extract 3 types of data :

  • URI - data from inside the request path. For instance, if your controller is bound to the /user/{id} URI, you can set the input argument {id} matching this uri part.
  • Query - data formatted at the end of the URL following the standard HTTP Query syntax.
  • URL encoded - data send inside the body of the request but following the HTTP Query syntax.
  • Multipart - data send inside the body of the request with a dedicated format. This format is not very lightweight but allows you to receive data as well as files.
  • JSON - data send inside the body as a json object ; each key being a variable name, each value its content. Note that the HTTP header 'Content-Type' must be set to application/json for the API to use it.
Format

The in field in each method contains as list of arguments where the key is the argument name, and the value defines how to manage the variable.

Variable names from URI or Query must be named accordingly :

  • the URI variable {id} from your request route must be named {id}.
  • the variable somevar in the Query has to be names GET@somevar.

Example

In this example we want 3 arguments :

[
	{
		"method": "PUT",
		"path": "/article/{id}",
		"scope": [["author"]],
		"info": "updates an article",
		"in": {
			"{id}":      { "info": "article id",          "type": "int",     "name": "article_id" },
			"GET@title": { "info": "new article title",   "type": "?string", "name": "title"      },
			"content":   { "info": "new article content", "type": "string"                        }
		},
		"out": {
			"id":      { "info": "updated article id",      "type": "uint"   },
			"title":   { "info": "updated article title",   "type": "string" },
			"content": { "info": "updated article content", "type": "string" }
		}
	}
]
  • the 1^st^ one is send at the end of the URI and is a number compliant with the int type checker. It is renamed article_id, this new name will be sent to the handler.
  • the 2^nd^ one is send in the query (e.g. http://host/uri?get-var=value). It must be a valid string or not given at all (the ? at the beginning of the type tells that the argument is optional) ; it will be named title.
  • the 3^rd^ can be send with a JSON body, in multipart or URL encoded it makes no difference and only give clients a choice over the technology to use. If not renamed, the variable will be given to the handler with the name content.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Builder added in v0.3.1

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

Builder for an aicra server

func (*Builder) AddType added in v0.3.1

func (b *Builder) AddType(t datatype.T)

AddType adds an available datatype to the api definition

func (*Builder) Bind added in v0.3.1

func (b *Builder) Bind(method, path string, fn interface{}) error

Bind a dynamic handler to a REST service

func (Builder) Build added in v0.3.1

func (b Builder) Build() (http.Handler, error)

Build a fully-featured HTTP server

func (*Builder) Setup added in v0.3.1

func (b *Builder) Setup(r io.Reader) error

Setup the builder with its api definition panics if already setup

type Server

type Server Builder

Server hides the builder and allows handling http requests

func (Server) ServeHTTP

func (server Server) ServeHTTP(res http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler and is called on each request

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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