update readme
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Adrien Marquès 2020-04-04 17:33:08 +02:00
parent 3c453e7f89
commit 658c66d2db
Signed by: xdrm-brackets
GPG Key ID: D75243CA236D825E
1 changed files with 77 additions and 96 deletions

173
README.md
View File

@ -7,40 +7,34 @@
[![Build Status](https://drone.xdrm.io/api/badges/go/aicra/status.svg)](https://drone.xdrm.io/go/aicra)
**Aicra** is a *configuration-driven* **web framework** written in Go that allows you to create a fully featured REST API.
Aicra is a *configuration-driven* REST API engine written in Go.
The whole management is done for you from a configuration file describing your API, you're left with implementing :
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](https://git.xdrm.io/go/articles-api)
The aicra server fulfills the `net/http` [Server interface](https://golang.org/pkg/net/http/#Server).
> A example project is available [here](https://git.xdrm.io/go/tiny-url-ex)
### Table of contents
## Table of contents
<!-- toc -->
- [I/ Installation](#i-installation)
- [II/ Development](#ii-development)
* [1) Main executable](#1-main-executable)
* [2) API Configuration](#2-api-configuration)
- [Definition](#definition)
+ [Input Arguments](#input-arguments)
- [1. Input types](#1-input-types)
- [2. Global Format](#2-global-format)
- [II/ Usage](#ii-usage)
* [1) Build a server](#1-build-a-server)
* [2) API Configuration](#2-api-configuration)
- [Definition](#definition)
+ [Input Arguments](#input-arguments)
- [1. Input types](#1-input-types)
- [2. Global Format](#2-global-format)
- [III/ Change Log](#iii-change-log)
<!-- tocstop -->
### I/ Installation
## I/ Installation
You need a recent machine with `go` [installed](https://golang.org/doc/install). This package has not been tested under the version **1.10**.
You need a recent machine with `go` [installed](https://golang.org/doc/install). This package has not been tested under the version **1.14**.
```bash
@ -50,95 +44,112 @@ 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/ Development
## II/ Usage
#### 1) Main executable
### 1) Build a server
Your main executable will declare and run the aicra server, it might look quite like the code below.
Here is some sample code that builds and sets up an aicra server using your api configuration file.
```go
package main
import (
"log"
"net/http"
"log"
"net/http"
"os"
"git.xdrm.io/go/aicra"
"git.xdrm.io/go/aicra/datatype"
"git.xdrm.io/go/aicra/datatype/builtin"
"git.xdrm.io/go/aicra"
"git.xdrm.io/go/aicra/api"
"git.xdrm.io/go/aicra/datatype/builtin"
)
func main() {
// 1. select your datatypes (builtin, custom)
var dtypes []datatype.T
dtypes = append(dtypes, builtin.AnyDataType{})
dtypes = append(dtypes, builtin.BoolDataType{})
dtypes = append(dtypes, builtin.UintDataType{})
dtypes = append(dtypes, builtin.StringDataType{})
builder := &aicra.Builder{}
// 2. create the server from the configuration file
server, err := aicra.New("path/to/your/api/definition.json", dtypes...)
// 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 built aicra server: %s\n", err)
log.Fatalf("cannot open config: %s", err)
}
// 3. bind your implementations
server.HandleFunc(http.MethodGet, "/path", func(req api.Request, res *api.Response){
// ... process stuff ...
res.SetError(api.ErrorSuccess());
})
// 4. extract to http server
httpServer, err := server.ToHTTPServer()
// pass your configuration
err = builder.Setup(config)
config.Close()
if err != nil {
log.Fatalf("cannot get to http server: %s", err)
log.Fatalf("invalid config: %s", err)
}
// 4. launch server
log.Fatal( http.ListenAndServe("localhost:8080", server) )
// 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
```go
type req struct{
Param1 int
Param3 *string // optional are pointers
}
type res struct{
Output1 string
Output2 bool
}
#### 2) API Configuration
func myHandler(r req) (*res, api.Error) {
err := doSomething()
if err != nil {
return nil, api.ErrorFailure
}
return &res{}, api.ErrorSuccess
}
```
The whole project 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](https://git.xdrm.io/go/tiny-url-ex/src/master/api.json). This file defines :
### 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](https://git.xdrm.io/go/articles-api/src/master/api.json). 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 (_i.e. for data types_)
- type of argument (_c.f. data types_)
- required/optional
- variable renaming
#### Format
###### Definition
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.
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. See [here](#input-arguments) for details. | |
| `out` | The list of output data that will be returned by your controllers. It has the same syntax as the `in` field but is only use for readability purpose and documentation. | |
| `in` | The list of arguments that the clients will have to provide. [Read more](#input-arguments). | |
| `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
###### 1. Input types
### Input Arguments
Input arguments defines what data from the HTTP request the method needs. Aicra is able to extract 3 types of data :
- **URI** - Curly Braces enclosed strings 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.
- **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](https://tools.ietf.org/html/rfc3986#section-3.4) syntax.
- **URL encoded** - data send inside the body of the request but following the [HTTP Query](https://tools.ietf.org/html/rfc3986#section-3.4) syntax.
- **Multipart** - data send inside the body of the request with a dedicated [format](https://tools.ietf.org/html/rfc2388#section-3). This format is not very lightweight but allows you to receive data as well as files.
@ -146,7 +157,7 @@ Input arguments defines what data from the HTTP request the method needs. Aicra
###### 2. Global Format
#### 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.
@ -159,10 +170,6 @@ The `in` field in each method contains as list of arguments where the key is the
In this example we want 3 arguments :
- 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](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`.
```json
[
{
@ -184,32 +191,6 @@ In this example we want 3 arguments :
]
```
### III/ Change Log
- [x] human-readable json configuration
- [x] nested routes (*i.e. `/user/:id:` and `/user/post/:id:`*)
- [x] nested URL arguments (*i.e. `/user/:id:` and `/user/:id:/post/:id:`*)
- [x] useful http methods: GET, POST, PUT, DELETE
- [x] manage URL, query and body arguments:
- [x] multipart/form-data (variables and file uploads)
- [x] application/x-www-form-urlencoded
- [x] application/json
- [x] required vs. optional parameters with a default value
- [x] parameter renaming
- [x] generic type check (*i.e. implement custom types alongside built-in ones*)
- [ ] built-in types
- [x] `any` - wildcard matching all values
- [x] `int` - see go types
- [x] `uint` - see go types
- [x] `float` - see go types
- [x] `string` - any text
- [x] `string(min, max)` - any string with a length between `min` and `max`
- [ ] `[a]` - array containing **only** elements matching `a` type
- [ ] `[a:b]` - map containing **only** keys of type `a` and values of type `b` (*a or b can be ommited*)
- [x] generic controllers implementation (shared objects)
- [x] response interface
- [x] log bound resources when building the aicra server
- [x] fail on check for unimplemented resources at server boot.
- [x] fail on check for unavailable types in api.json at server boot.
- 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](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`.