Giter Site home page Giter Site logo

apig's Introduction

⚠️ This package is now officially maintained by @shimastripe at shimastripe/apig. ⚠️

This repository exists only for preventing existing codes from failing to resolve package, and will no longer be maintained. Please send a pull request to the new repository.


apig: Golang RESTful API Server Generator

Build Status

apig is an RESTful API server generator.

  • Input: Model definitions based on gorm annotated struct
  • Output: RESTful JSON API server using gin including tests and documents

Contents

How to build and install

Go 1.6 or higher is required.

After installing required version of Go, you can build and install apig by

$ go get -d -u github.com/wantedly/apig
$ cd $GOPATH/src/github.com/wantedly/apig
$ make
$ make install

make generates binary into bin/apig. make install put it to $GOPATH/bin.

How to use

1. Generate boilerplate

First, creating by apig new command.

$ apig new -u wantedly apig-sample

generates Golang API server boilerplate under $GOPATH/src/gihhub.com/wantedly/apig-sample. apig supports two database engines; SQLite (sqlite) and PostgreSQL (postgres) and Mysql (mysql). You can specify this by -d, -database option.

Available command line options of apig new command are:

Option Description Required Default
-d, -database Database engine sqlite
-n, -namespace Namespace of API (empty)
-u, -user Username github username
--vcs VCS github.com

2. Write model code

Second, write model definitions under models/. For example, user and email model is like below:

// models/user.go
package models

import "time"

type User struct {
	ID        uint       `gorm:"primary_key;AUTO_INCREMENT" json:"id" form:"id"`
	Name      string     `json:"name" form:"name"`
	Emails    []Email    `json:"emails" form:"emails"`
	CreatedAt *time.Time `json:"created_at" form:"created_at"`
	UpdatedAt *time.Time `json:"updated_at" form:"updated_at"`
}
// models/email.go
package models

type Email struct {
	ID      uint   `gorm:"primary_key;AUTO_INCREMENT" json:"id" form:"id"`
	UserID  uint   `json:"user_id" form:"user_id"`
	Address string `json:"address" form:"address"`
	User    *User  `json:"user form:"user`
}

This models are based on gorm structure. Please refer gorm document to write detailed models.

3. Generate controllers, tests, documents etc. based on models.

Third, run the command:

apig gen

It creates all necessary codes to provide RESTful endpoints of models.

4. Build and run server

Finally, just build as normal go code.

$ go get ./...
$ go build -o bin/server

After that just execute the server binary. For the first time, you may want to use AUTOMIGRATE=1 when running the server.

$ AUTOMIGRATE=1 bin/server

When AUTOMIGRATE=1, the db tables are generated automatically. After that, you can run the server just executing the command:

$ bin/server

The server runs at http://localhost:8080.

By default, use the port 8080. If you change the port, set environment variables.

$ PORT=3000 bin/server

The server runs at http://localhost:3000.

Usage

new command

new command tells apig to generate API server skeleton.

$ apig new NAME

gen command

gen command tells apig to generate files (routes, controllers, documents...) from gorm model files you wrote.

You MUST run this command at the directory which was generated by new command.

$ apig gen

API Document

API Documents are generated automatically in docs/ directory in the form of API Blueprint.

docs
├── email.apib
├── index.apib
└── user.apib

Aglio is an API Blueprint renderer. Aglio can be installed by

$ npm install -g aglio

You can generate HTML files and run live preview server.

// html file
$ aglio -i index.apib  -o index.html

// running server on localhost:3000
$ aglio -i index.apib --server

index.apib includes other files in your blueprint.

API server specification

Endpoints

Each resource has 5 RESTful API endpoints. Resource name is written in the plural form.

Endpoint Description Example (User resource)
GET /<resources> List items GET /users List users
POST /<resources> Create new item POST /users Create new user
GET /<resources>/{id} Retrieve the item GET /users/1 Get the user which ID is 1
PUT /<resources>/{id} Update the item PUT /users/1 Update the user which ID is 1
DELETE /<resources>/{id} Delete the item DELETE /users/1 Delete the user which ID is 1

Available URL parameters

GET /<resources> and GET /<resources>/{id}

Parameter Description Default Example
fields= Fields to receive All fields name,emails.address
preloads= Nested resources to preload (empty) emails,profile
pretty= Prettify JSON response false true

GET /<resources> only

Parameter Description Default Example
stream= Return JSON in streaming format false true
q[field_name]= A unique query parameter for each field for filtering (empty) q[id]=1,2,5, q[admin]=true&q[registered]=true
sort= Retrieves a list in order of priority. + or (none) : ascending. - : descending (empty) id, -age, id,-created_at
limit= Maximum number of items 25 50
page= Page to receive 1 3
last_id= Beginning ID of items (empty) 1
order= Order of items desc asc
v= API version (empty) 1.2.0

Data Type

Request

API server accepts the form of JSON or Form.

application/json

curl -X POST http://localhost:8080/resources \
     -H "Content-type: application/json" \
     -d '{"field":"value"}'

application/x-www-form-urlencoded

curl -X POST http://localhost:8080/users \
     -d 'field=value'

multipart/form-data

curl -X POST http://localhost:8080/users \
     -F 'field=value'

Response

Response data type is always application/json.

Pagination

API server supports 2 pagination types.

Offset-based pagination

Retrieve items by specifying page number and the number of items per page.

For example:

http://example.com/api/users?limit=5&page=2
+---------+---------+---------+---------+---------+---------+---------+
| ID: 5   | ID: 6   | ID: 7   | ID: 8   | ID: 9   | ID: 10  | ID: 11  |
+---------+---------+---------+---------+---------+---------+---------+
          |                                                 |
 Page 1 ->|<-------------------- Page 2 ------------------->|<- Page 3

Response header includes Link header.

Link:   <http://example.com/api/users?limit=5&page=3>; rel="next",
        <http://example.com/api/users?limit=5&page=1>; rel="prev"

ID/Time-based pagination

Retrieve items by specifying range from a certain point.

For example:

http://example.com/api/users?limit=5&last_id=100&order=desc
+---------+---------+---------+---------+---------+---------+---------+
| ID: 94  | ID: 95  | ID: 96  | ID: 97  | ID: 98  | ID: 99  | ID: 100 |
+---------+---------+---------+---------+---------+---------+---------+
          |               5 items (ID < 100)                |
          |<------------------------------------------------|

Response header includes Link header.

Link:   <http://example.com/api/users?limit=5&last_id=95&order=desc>; rel="next"

Versioning

API server uses Semantic Versioning for API versioning.

There are 2 methods to specify API version.

Request header

Generally we recommend to include API version in Accept header.

Accept: application/json; version=1.0.0

URL parameter

You can also include API version in URL parameter. This is userful for debug on browser or temporary use,

http://example.com/api/users?v=1.0.0

This method is prior to request header method.

License

MIT License

apig's People

Contributors

awakia avatar chansuke avatar dtan4 avatar koudaiii avatar luvtechno avatar mattkanwisher avatar munisystem avatar shimastripe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apig's Issues

panic: runtime error: index out of range

WHY

To reproduce, generated api server receive http request not including Accept header.

sample code:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    url := "http://localhost:8080/api/users"
    req, _ := http.NewRequest(
        "GET",
        url,
        nil,
    )
    client := new(http.Client)
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    b, _ := ioutil.ReadAll(resp.Body)
    fmt.Printf("Response Body: %v", string(b))
}

api server logs:

2016/08/25 11:39:34 [Recovery] panic recovered:
GET /api/users HTTP/1.1
Host: localhost:3000
Accept-Encoding: gzip
User-Agent: Go-http-client/1.1


runtime error: index out of range
/usr/local/Cellar/go/1.6.2/libexec/src/runtime/panic.go:443 (0x402ce99)
    gopanic: reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
/usr/local/Cellar/go/1.6.2/libexec/src/runtime/panic.go:27 (0x402b325)
    panicindex: panic(indexError)
/Users/muni/.go/src/github.com/wantedly/apig/_example/version/version.go:12 (0x43cb5e7)
    New: header := c.Request.Header["Accept"][0]
/Users/muni/.go/src/github.com/wantedly/apig/_example/controllers/user.go:16 (0x42d6b83)
    GetUsers: ver, err := version.New(c)
/Users/muni/.go/src/github.com/gin-gonic/gin/context.go:97 (0x40755da)
    (*Context).Next: c.handlers[c.index](c)
/Users/muni/.go/src/github.com/wantedly/apig/_example/middleware/set_db.go:12 (0x41072bb)
    SetDBtoContext.func1: c.Next()
/Users/muni/.go/src/github.com/gin-gonic/gin/context.go:97 (0x40755da)
    (*Context).Next: c.handlers[c.index](c)
/Users/muni/.go/src/github.com/gin-gonic/gin/recovery.go:45 (0x4087ef1)
    RecoveryWithWriter.func1: c.Next()
/Users/muni/.go/src/github.com/gin-gonic/gin/context.go:97 (0x40755da)
    (*Context).Next: c.handlers[c.index](c)
/Users/muni/.go/src/github.com/gin-gonic/gin/logger.go:63 (0x408718a)
    LoggerWithWriter.func1: c.Next()
/Users/muni/.go/src/github.com/gin-gonic/gin/context.go:97 (0x40755da)
    (*Context).Next: c.handlers[c.index](c)
/Users/muni/.go/src/github.com/gin-gonic/gin/gin.go:284 (0x407c432)
    (*Engine).handleHTTPRequest: context.Next()
/Users/muni/.go/src/github.com/gin-gonic/gin/gin.go:265 (0x407c067)
    (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/Cellar/go/1.6.2/libexec/src/net/http/server.go:2081 (0x41e3d8e)
    serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/Cellar/go/1.6.2/libexec/src/net/http/server.go:1472 (0x41e063e)
    (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/Cellar/go/1.6.2/libexec/src/runtime/asm_amd64.s:1998 (0x405cf41)
    goexit: BYTE    $0x90   // NOP

[GIN] 2016/08/25 - 11:39:34 | 500 |      1.4057ms | ::1 |   GET     /api/users

WHAT

If http request not including Accept header, c.Request.Header["Accept"] is empty.
(type: map[string][]string)
ref. https://github.com/wantedly/apig/blob/master/_example/version/version.go#L12

I think we should check map length and return that gist using response body.
What do you think?

Error installing

When I run make I get an error:

go generate
main.go:5: running "go-bindata": exec: "go-bindata": executable file not found in $PATH
Makefile:11: recipe for target 'bin/apig' failed
make: *** [bin/apig] Error 1

Support structs that use gorm.Model

Currently any models that use gorm.Model rather than specifying ID manually are ignored. Models should be picked up when gorm.Model is used.

Example

type Customer struct {
	gorm.Model
	FirstName string
	LastName  string
}

JSON output should pretty format

WHY

Heroku-api-design says "Keep JSON minified in all responses".
https://geemus.gitbooks.io/http-api-design/content/en/responses/keep-json-minified-in-all-responses.html

So pretty JSON output is easy to read for human.

WHAT

gin has pretty JSON output method.

c.JSON(200, fieldMap)
c.IndentedJSON(200, fieldMap)

However, gin warned,

WARNING: we recommend to use this only for development propuses since printing pretty JSON is more CPU and bandwidth consuming. Use Context.JSON() instead.

I think it does not cost so much to convert pretty JSON output.

How do you think?

Running apig gen panics if a struct field does not have a tag.

To reproduce, follow the readme tutorial until the point where you make models/user.go:

// models/user.go
package models

import "time"

type User struct {
    ID        uint       `gorm:"primary_key;AUTO_INCREMENT" json:"id"`
    Name      string     `json:"name"`
    Emails    []Email    `json:"emails"`
    CreatedAt *time.Time `json:"created_at"`
    UpdatedAt *time.Time `json:"updated_at"`
}

and remove the struct tag from a field:

// models/user.go
package models

import "time"

type User struct {
    ID        uint       `gorm:"primary_key;AUTO_INCREMENT" json:"id"`
    Name      string     `json:"name"`
    Emails    []Email    `json:"emails"`
    CreatedAt *time.Time `json:"created_at"`
    UpdatedAt *time.Time
}

Define alias for db package in controller

WHY

Now we use db package as it is in controller. However, we also define variable db for database object.
It causes conflict of names.

https://github.com/wantedly/api-server-generator/blob/2aa41e780aef0b917d4a885dcfd979a8c3138934/_examples/simple/controllers/user.go#L3-L13

import (
    "net/http"

    "github.com/wantedly/api-server-generator/examples/simple/db"
    "github.com/wantedly/api-server-generator/examples/simple/models"

    "github.com/gin-gonic/gin"
)

func GetUsers(c *gin.Context) {
    db := db.DBInstance(c)

WHAT

Define alias (e.g. dbpkg) for db package to avoid name conflicts.

import (
    "net/http"

    dbpkg "github.com/wantedly/api-server-generator/examples/simple/db"
    "github.com/wantedly/api-server-generator/examples/simple/models"

    "github.com/gin-gonic/gin"
)

Generate documents in API Blueprint

WHY

To understand the specification of genereted APIs, we have to prepare the documents.

WHAT

Generate documents with programs.

API Blueprint is better format of API documents. This format is easy to read and edit because this is based on Markdown. Also, HTML document and API tests are generated easily by using other tools.

Directory structure is below:

|-- docs
    |-- index.apib
    |-- user.apib
    `-- profile.apib

API Blueprint documents are generated inside docs directory. index.apib is the top page of documents and has the links to each API pages.

Multiple files

Currently there is no specification of multiple (modulized) files in API Blueprint. There are some solutions by using document generators.

We are going to user aglio for document generator. Therefore the links to other document should be written as below:

# Gist Fox API

# Group Gist

<!-- include(blueprint/gist.apib) -->

<!-- include(blueprint/gists.apib) -->

REF

[2016-07-22] Points need to be fixed

About Makefile

  • solved

(priority low)

I pretty doubt about necessity of make deps

make deps
make
make install

flow seems really unusual for me. Is there any major app which uses this flow?

If we create makefile, we should follow make standard flow:

make
make install

About Target Directory

  • solved
apig new apig-sample
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/README.md
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/.gitignore
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/main.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/db/db.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/db/pagination.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/router/router.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/middleware/set_db.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/server/server.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/helper/field.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/version/version.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/version/version_test.go
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/controllers/.gitkeep
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/docs/.gitkeep
    create /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample/models/.gitkeep
===> Created /Users/awakia/src/github.com/Naoyoshi Aikawa/apig-sample
git config --global github.user awakia

solves this problem, but this is tooooo confusing.

We should somehow solve this problem by asking directory or accepting argument about directory

About README

  • solved

The instruction in README is not understandable...
Maybe users cannot create servers like what we expected.

We should explain more where to write initial file and gorm model file

gen command

gen command tells apig to generate files (routes, controllers, documents...) from gorm model files you wrote.

Bugs

  • in controller

Nest foreign key relations

cc @awakia @dtan4 @munisystem

WHY

https://geemus.gitbooks.io/http-api-design/content/en/responses/nest-foreign-key-relations.html

Summary Goes Here

Serialize foreign key references with a nested object, e.g.:

{
  "name": "service-production",
  "owner": {
    "id": "5d8201b0..."
  },
  // ...
}

Instead of e.g.:

{
  "name": "service-production",
  "owner_id": "5d8201b0...",
  // ...
}

This approach makes it possible to inline more information about the related resource without having to change the structure of the response or introduce more top-level response fields, e.g.:

{
  "name": "service-production",
  "owner": {
    "id": "5d8201b0...",
    "name": "Alice",
    "email": "[email protected]"
  },
  // ...
}
## WHAT

Then now apig works this.

{
  "name": "service-production",
  "owner_id": "5d8201b0...",
  "owner": null
}

so I think apig should work in this way.

{
  "name": "service-production",
  "owner": {
    "id": "5d8201b0...",
  }
}

I want to hear everyone's opinion.

HOW

  • How to identify foreign key ?

gorm has gorm:"ForeignKey:Profile" tag.

But gorm works json:profile_id tag as gorm:"ForeignKey:Profile" without gorm:"ForeignKey:Profile".

ProfileID   uint      `json:"profile_id"`
// => ProfileID   uint      `json:"profile_id" gorm:"ForeignKey:Profile"`

So I don't know what is better that gorm:"ForeignKey:XXX" was mandatory.

Rename command to the shorter one

WHY

api-server-generator command is too long to type, I think.
Command name should be short and easy to remember as possible.

WHAT

Rename api-server-generator command to the shorter one.

IMO apig (abbreviation of API-server-Generator) seems to be good. This is short enough and easy to remember. Additionally, at this time there seems to be no project named apig on GitHub. This name has uniqueness too.

controllers/root.go is not generated

WHY

execute apig gen:

~/.go/src/github.com/munisystem/sample go(v1.6.2) ♦ apig gen
    [create /Users/muni/.go/src/github.com/munisystem/sample/docs/user.apib] %!s(MISSING)
    [create /Users/muni/.go/src/github.com/munisystem/sample/controllers/user.go] %!s(MISSING)
    [create /Users/muni/.go/src/github.com/munisystem/sample/docs/index.apib] %!s(MISSING)
    [update /Users/muni/.go/src/github.com/munisystem/sample/router/router.go] %!s(MISSING)
    [update /Users/muni/.go/src/github.com/munisystem/sample/db/db.go] %!s(MISSING)
    [update /Users/muni/.go/src/github.com/munisystem/sample/README.md] %!s(MISSING)
===> Generated... 

not generated controller/root.go:

~/.go/src/github.com/munisystem/sample go(v1.6.2) ♦ go build
# github.com/munisystem/sample/router
router/router.go:10: undefined: controllers.APIEndpoints
~/.go/src/github.com/munisystem/sample go(v1.6.2) ♦
~/.go/src/github.com/munisystem/sample go(v1.6.2) ♦
~/.go/src/github.com/munisystem/sample go(v1.6.2) ♦ ls controllers/
user.go

WHAT

Add to run generateRootController() when execute apig gen.

Is this project dead ?

Hi contributors,

Look like this project has been stopped. Is there any alternative ? Or please someone take control to continue contribute this project.

Design Document

Why to create

When creating API server, people sometimes break restfulness because of the business logics. It is not easy for human to create precisely restful api. So why not generate automatically by computer? The goal of this project is to create api server generator based on model definition, which is basically same with db schema.

What to create

The API Server Generator below:

  • Input: model definition based on gorm annotated struct
  • Output: whole restful json api server including tests and documents

Example Input

Add model definitions under models/. For example, models/user.go is like below:

package models

import "time"

type User struct {
    ID          uint      `gorm:"primary_key" json:"id"`
    Name        string    `json:"name"`
    AccountName string    `json:"account_name"`
    Email       string    `json:"email"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

Interaction with human

We want to stick with RESTFULLNESS, our generator output is not editable. In other words, our generator is not like rails generator.

In rails, generator creates boilerplates by the command rails new or rails generate. After that programmer edit such boilerplates to make a complete server. Rails experience is great, but there are 2 big problems when creating solid api server.

  1. Human can break whole structure
  2. Rails cannot regenerate additional elements when needed
    • i.e. When adding a field to model, rails cannot insert such fields to generated contents.

So we decided that generated codes are not editable. Thanks to this design, regenerating whole contents just works even when adding fields.
When some tweaks are needed, we supply before and after hooks.

  • Before hook can edit request parameters
  • After hook can edit json response

Accept: application/vnd.{{ .User }}+json seems too complecated

WHY

Since Heroku API Design says

It is best to provide version specification in the headers, with other metadata, using the Accept header with a custom content type, e.g.:

Accept: application/vnd.heroku+json; version=3

We decided to use

Accept: application/vnd.{{ .User }}+json; version={{ .Version }}

Style.

Though, heroku only says accept header is good, but not about application/vnd.

This style makes it difficult to parse version a little bit for api provider.
For api user, considering vnd.XXXX part each time to request version is also bothersome.

WHAT

Let's consider much simpler solution.

Idea 1

Accept: application/json; version=1.0

http://www.django-rest-framework.org/api-guide/versioning/#acceptheaderversioning

At the site there is the reason why provider should use vnd

Strictly speaking the json media type is not specified as including additional parameters. If you are building a well-specified public API you might consider using a vendor media type. To do so, configure your renderers to use a JSON based renderer with a custom media type:

Idea2

X-API-Version: 1.0

http://kenn.hatenablog.com/entry/2014/03/06/105249

This actually easier to parse. I don't know cons for this though.

Other Style

Accept: application/vnd.steveklabnik-v2+json

Filtering with q[field_name] not working if field has different name in json

Filtering with q[field_name] not working if field has different name in json, for example

type User struct {
    ID          uint       `gorm:"primary_key;AUTO_INCREMENT" json:"id" form:"id"`
    Pid         uint `json:"uid" form:"uid"`
    CreatedAt   *time.Time `json:"created_at" form:"created_at"`
    UpdatedAt   *time.Time `json:"updated_at" form:"updated_at"`
}

GET http://localhost:8080/users?q[uid]=42
gives me

{"error":"pq: column \"uid\" does not exist"}

but GET http://localhost:8080/users?q[pid]=42 is OK

it's not a big deal for me, but maybe you should mention it in docs, that q[field_name] is more likely q[column_name]

Rewrite apig command line parser tcnksm/gcli

WHY

Current command line parser implementation is unfortunately horrible...

For example,

apig new --help
apig new -u wantedly sample-server

cannot be parsed as expected.
It just generate the code to --help or -u directory.

WHAT

There is good tool to write CLI like https://github.com/tcnksm/gcli

Why don't we use it? I personally like mitchellh_cli style, but any style of gcli options are acceptable.

@munisystem Please rewrite the code with @dtan4 's help

generate boilerplate command is wrong?

in README:

  1. Generate boilerplate

First, creating by apig new command.

$ apig new -u wantedly -v github.com apig-sample

It was actually executed:

[ShotaKubota] ~/src/github.com/wantedly
(*'-') < apig new -u wantedly -v github.com apig-sample
apig version 0.1.0

not created apig-sample directory:

[ShotaKubota] ~/src/github.com/wantedly
(*'-') < ls -l
total 0
drwxr-xr-x  24 ShotaKubota  staff  816  8  4 20:51 apig

exclude -v github.com:

[ShotaKubota] ~/src/github.com/wantedly
(*'-') < apig new -u wantedly apig-sample
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/models/.gitkeep
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/docs/.gitkeep
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/version/version.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/version/version_test.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/main.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/README.md
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/helper/field_test.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/controllers/.gitkeep
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/middleware/set_db.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/helper/field.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/db/db.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/.gitignore
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/server/server.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/router/router.go
    create /Users/ShotaKubota/src/github.com/wantedly/apig-sample/db/pagination.go
===> Created /Users/ShotaKubota/src/github.com/wantedly/apig-sample

created apig-sample directory:

[ShotaKubota] ~/src/github.com/wantedly
(*'-') < ls -l
total 0
drwxr-xr-x  24 ShotaKubota  staff  816  8  4 20:51 apig
drwxr-xr-x  14 ShotaKubota  staff  476  8  4 20:58 apig-sample

apig gen command shows : Invalid import path

I was trying to implement same plugin in go.

When i run apig run command on my windows machine, it shows "Invalid import path : github.com\wantedly\Test "

Please help me to rectify this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.