api2go

package module
v2.5.24+incompatible Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: MIT Imports: 19 Imported by: 13

README

api2go

GoDoc Build Status Coverage Status Go Report Card

A JSON API Implementation for Go, to be used e.g. as server for Ember Data.

TOC

Installation

For the complete api2go package use:

go get github.com/artpar/api2go

If you only need marshalling and/or unmarshalling:

go get github.com/artpar/api2go/jsonapi 

Basic functionality

Api2go will Marshal/Unmarshal exactly like the internal json package from Go with one addition: It will decorate the Marshalled json with jsonapi meta objects. Jsonapi wraps the payload inside an attributes object. The rest is just Meta-Data which will be generated by api2go.

So let's take this basic example:

type Article struct {
	ID    string
	Title string `json:"title"`
}

Would json.Marshal into this Json:

{
  "ID": "Some-ID",
  "title": "the title"
}

For api2go, you have to ignore tag the ID field and then the result could be something like this:

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "/articles/1/relationships/author",
        "related": "/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
  }
}

All the additional information is retrieved by implementing some interfaces.

Examples

Interfaces to implement

For the following query and result examples, imagine the following 2 structs which represent a posts and comments that belong with a has-many relation to the post.

type Post struct {
  ID          int       `json:"-"`  // Ignore ID field because the ID is fetched via the
                                    // GetID() method and must not be inside the attributes object.
  Title       string    `json:"title"`
  Comments    []Comment `json:"-"` // this will be ignored by the api2go marshaller
  CommentsIDs []int     `json:"-"` // it's only useful for our internal relationship handling
}

type Comment struct {
  ID   int    `json:"-"`
  Text string `json:"text"`
}

You must at least implement the MarshalIdentifier interface, which is the one for marshalling/unmarshalling the primary ID of the struct that you want to marshal/unmarshal. This is because of the huge variety of types that you could use for the primary ID. For example a string, a UUID or a BSON Object for MongoDB etc...

In the Post example struct, the ID field is ignored because api2go will use the GetID method that you implemented for your struct to fetch the ID of the struct. Every field inside a struct will be marshalled into the attributes object in the json. In our example, we just want to have the Title field there.

Don't forget to name all your fields with the json:"yourName" tag.

Responder
type Responder interface {
	Metadata() map[string]interface{}
	Result() interface{}
	StatusCode() int
}

The Responder interface must be implemented if you are using our API. It contains everything that is needed for a response. You can see an example usage of it in our example project.

EntityNamer
type EntityNamer interface {
	GetName() string
}

EntityNamer is an optional interface. Normally, the name of a struct will be automatically generated in its plural form. For example if your struct has the type Post, its generated name is posts. And the url for the GET request for post with ID 1 would be /posts/1.

If you implement the GetName() method and it returns special-posts, then this would be the name in the type field of the generated json and also the name for the generated routes.

Currently, you must implement this interface, if you have a struct type that consists of multiple words and you want to use a hyphenized name. For example UnicornPost. Our default Jsonifier would then generate the name unicornPosts. But if you want the recommended name, you have to implement GetName

func (s UnicornPost) GetName() string {
	return "unicorn-posts"
}
MarshalIdentifier
type MarshalIdentifier interface {
	GetID() string
}

Implement this interface to marshal a struct.

UnmarshalIdentifier
type UnmarshalIdentifier interface {
	SetID(string) error
}

This is the corresponding interface to MarshalIdentifier. Implement this interface in order to unmarshal incoming json into a struct.

Marshalling with References to other structs

For relationships to work, there are 3 Interfaces that you can use:

type MarshalReferences interface {
	GetReferences() []Reference
}

// MarshalLinkedRelations must be implemented if there are references and the reference IDs should be included
type MarshalLinkedRelations interface {
	MarshalReferences
	MarshalIdentifier
	GetReferencedIDs() []ReferenceID
}

// MarshalIncludedRelations must be implemented if referenced structs should be included
type MarshalIncludedRelations interface {
	MarshalReferences
	MarshalIdentifier
	GetReferencedStructs() []MarshalIdentifier
}

Implementing those interfaces is not mandatory and depends on your use cases. If your API has any relationships, you must at least implement MarshalReferences and MarshalLinkedRelations.

MarshalReferences must be implemented in order for api2go to know which relations are possible for your struct.

MarshalLinkedRelations must be implemented to retrieve the IDs of the relations that are connected to this struct. This method could also return an empty array, if there are currently no relations. This is why there is the MarshalReferences interface, so that api2go knows what is possible, even if nothing is referenced at the time.

In addition to that, you can implement MarshalIncludedRelations which exports the complete referenced structs and embeds them in the json result inside the included object.

That way you can choose how you internally manage relations. So, there are no limits regarding the use of ORMs.

Unmarshalling with references to other structs

Incoming jsons can also contain reference IDs. In order to unmarshal them correctly, you have to implement the following interfaces. If you only have to-one relationships, the UnmarshalToOneRelations interface is enough.

// UnmarshalToOneRelations must be implemented to unmarshal to-one relations
type UnmarshalToOneRelations interface {
	SetToOneReferenceID(name, ID string) error
}

// UnmarshalToManyRelations must be implemented to unmarshal to-many relations
type UnmarshalToManyRelations interface {
	SetToManyReferenceIDs(name string, IDs []string) error
}

If you need to know more about how to use the interfaces, look at our tests or at the example project.

Manual marshalling / unmarshalling

Please keep in mind that this only works if you implemented the previously mentioned interfaces. Manual marshalling and unmarshalling makes sense, if you do not want to use our API that automatically generates all the necessary routes for you. You can directly use our sub-package github.com/artpar/api2go/jsonapi

comment1 = Comment{ID: 1, Text: "First!"}
comment2 = Comment{ID: 2, Text: "Second!"}
post = Post{ID: 1, Title: "Foobar", Comments: []Comment{comment1, comment2}}

json, err := jsonapi.Marshal(post)

will yield

{
  "data": [
    {
      "id": "1",
      "type": "posts",
      "attributes": {
        "title": "Foobar"
      },
      "relationships": {
        "comments": {
          "data": [
            {
              "id": "1",
              "type": "comments"
            },
            {
              "id": "2",
              "type": "comments"
            }
          ]
        }
      }
    }
  ],
  "included": [
    {
      "id": "1",
      "type": "comments",
      "attributes": {
        "text": "First!"
      }
    },
    {
      "id": "2",
      "type": "comments",
      "attributes": {
        "text": "Second!"
      }
    }
  ]
}

You can also use jsonapi.MarshalWithURLs to automatically generate URLs for the rest endpoints that have a version and BaseURL prefix. This will generate the same routes that our API uses. This adds self and related fields for relations inside the relationships object.

Recover the structure from above using. Keep in mind that Unmarshalling with included structs does not work yet. So Api2go cannot be used as a client yet.

var posts []Post
err := jsonapi.Unmarshal(json, &posts)
// posts[0] == Post{ID: 1, Title: "Foobar", CommentsIDs: []int{1, 2}}

SQL Null-Types

When using a SQL Database it is most likely you want to use the special SQL-Types from the database/sql package. These are

  • sql.NullBool
  • sql.NullFloat64
  • sql.NullInt64
  • sql.NullString

The Problem is, that they internally manage the null value behavior by using a custom struct. In order to Marshal and Unmarshal these values, it is required to implement the json.Marshaller and json.Unmarshaller interfaces of the go standard library.

But you dont have to do this by yourself! There already is a library that did the work for you. We recommend that you use the types of this library: http://gopkg.in/guregu/null.v2/zero

In order to use omitempty with those types, you need to specify them as pointers in your struct.

Using api2go with the gin framework

If you want to use api2go with gin you need to use a different router than the default one. Get the according adapter using:

go get -tags=gingonic github.com/artpar/api2go

Currently the supported tags are: gingonic,gorillamux, or echo.

After that you can bootstrap api2go the following way:

  import (
    "github.com/gin-gonic/gin"
    "github.com/artpar/api2go"
    "github.com/artpar/api2go/routing"
    "github.com/artpar/api2go/examples/model"
    "github.com/artpar/api2go/examples/resource"
    "github.com/artpar/api2go/examples/storage"
  )

  func main() {
    r := gin.Default()
    api := api2go.NewAPIWithRouting(
      "api",
      api2go.NewStaticResolver("/"),
      routing.Gin(r),
    )

    userStorage := storage.NewUserStorage()
    chocStorage := storage.NewChocolateStorage()
    api.AddResource(model.User{}, resource.UserResource{ChocStorage: chocStorage, UserStorage: userStorage})
    api.AddResource(model.Chocolate{}, resource.ChocolateResource{ChocStorage: chocStorage, UserStorage: userStorage})

    r.GET("/ping", func(c *gin.Context) {
      c.String(200, "pong")
    })
    r.Run(":8080")
  }

Keep in mind that you absolutely should map api2go under its own namespace to not get conflicts with your normal routes.

If you need api2go with any different go framework, just send a PR with the according adapter :-)

Building a REST API

First, write an implementation of either api2go.ResourceGetter, api2go.ResourceCreator, api2go.ResourceUpdater, api2go.ResourceDeleter, or any combination of them. You can also write an implementation the CRUD interface which embed all of them. You have to implement at least one of these 4 methods:

type fixtureSource struct {}

// FindOne returns an object by its ID
// Possible success status code 200
func (s *fixtureSource) FindOne(ID string, r api2go.Request) (Responder, error) {}

// Create a new object. Newly created object/struct must be in Responder.
// Possible status codes are:
// - 201 Created: Resource was created and needs to be returned
// - 202 Accepted: Processing is delayed, return nothing
// - 204 No Content: Resource created with a client generated ID, and no fields were modified by
//   the server
func (s *fixtureSource) Create(obj interface{}, r api2go.Request) (Responder, err error) {}

// Delete an object
// Possible status codes are:
// - 200 OK: Deletion was a success, returns meta information, currently not implemented! Do not use this
// - 202 Accepted: Processing is delayed, return nothing
// - 204 No Content: Deletion was successful, return nothing
func (s *fixtureSource) Delete(id string, r api2go.Request) (Responder, err error) {}

// Update an object
// Possible status codes are:
// - 200 OK: Update successful, however some field(s) were changed, returns updates source
// - 202 Accepted: Processing is delayed, return nothing
// - 204 No Content: Update was successful, no fields were changed by the server, return nothing
func (s *fixtureSource) Update(obj interface{}, r api2go.Request) (Responder, err error) {}

If you want to return a jsonapi compatible error because something went wrong inside the CRUD methods, you can use our HTTPError struct, which can be created with NewHTTPError. This allows you to set the error status code and add as many information about the error as you like. See: jsonapi error

To fetch all objects of a specific resource you can choose to implement one or both of the following interfaces:

type FindAll interface {
	// FindAll returns all objects
	FindAll(req Request) (Responder, error)
}

type PaginatedFindAll interface {
	PaginatedFindAll(req Request) (totalCount uint, response Responder, err error)
}

FindAll returns everything. You could limit the results only by using Query Params which are described here

PaginatedFindAll can also use Query Params, but in addition to that it does not need to send all objects at once and can split up the result with pagination. You have to return the total number of found objects in order to let our API automatically generate pagination links. More about pagination is described here

You can then create an API:

api := api2go.NewAPI("v1")
api.AddResource(Post{}, &PostsSource{})
http.ListenAndServe(":8080", api.Handler())

Instead of api2go.NewAPI you can also use api2go.NewAPIWithBaseURL("v1", "http://yourdomain.com") to prefix all automatically generated routes with your domain and protocoll.

This generates the standard endpoints:

OPTIONS /v1/posts
OPTIONS /v1/posts/<id>
GET     /v1/posts
POST    /v1/posts
GET     /v1/posts/<id>
PATCH   /v1/posts/<id>
DELETE  /v1/posts/<id>
GET     /v1/posts/<id>/comments            // fetch referenced comments of a post
GET     /v1/posts/<id>/relationships/comments      // fetch IDs of the referenced comments only
PATCH   /v1/posts/<id>/relationships/comments      // replace all related comments

// These 2 routes are only created for to-many relations that implement EditToManyRelations interface
POST    /v1/posts/<id>/relationships/comments      // Add a new comment reference, only for to-many relations
DELETE  /v1/posts/<id>/relationships/comments      // Delete a comment reference, only for to-many relations

For the last two generated routes, it is necessary to implement the jsonapi.EditToManyRelations interface.

type EditToManyRelations interface {
	AddToManyIDs(name string, IDs []string) error
	DeleteToManyIDs(name string, IDs []string) error
}

All PATCH, POST and DELETE routes do a FindOne and update the values/relations in the previously found struct. This struct will then be passed on to the Update method of a resource struct. So you get all these routes "for free" and just have to implement the ResourceUpdater Update method.

Query Params

To support all the features mentioned in the Fetching Resources section of Jsonapi: http://jsonapi.org/format/#fetching

If you want to support any parameters mentioned there, you can access them in your Resource via the api2go.Request Parameter. This currently supports QueryParams which holds all query parameters as map[string][]string unfiltered. So you can use it for:

  • Filtering
  • Inclusion of Linked Resources
  • Sparse Fieldsets
  • Sorting
  • Aything else you want to do that is not in the official Jsonapi Spec
type fixtureSource struct {}

func (s *fixtureSource) FindAll(req api2go.Request) (Responder, error) {
  for key, values range req.QueryParams {
    ...
  }
  ...
}

If there are multiple values, you have to separate them with a comma. api2go automatically slices the values for you.

Example Request
GET /people?fields=id,name,age

req.QueryParams["fields"] contains values: ["id", "name", "age"]
Using Pagination

Api2go can automatically generate the required links for pagination. Currently there are 2 combinations of query parameters supported:

  • page[number], page[size]
  • page[offset], page[limit]

Pagination is optional. If you want to support pagination, you have to implement the PaginatedFindAll method in you resource struct. For an example, you best look into our example project.

Example request

GET /v0/users?page[number]=2&page[size]=2

would return a json with the top level links object

{
  "links": {
    "first": "http://localhost:31415/v0/users?page[number]=1&page[size]=2",
    "last": "http://localhost:31415/v0/users?page[number]=5&page[size]=2",
    "next": "http://localhost:31415/v0/users?page[number]=3&page[size]=2",
    "prev": "http://localhost:31415/v0/users?page[number]=1&page[size]=2"
  },
  "data": [...]
}

The IDs of a relationship can be fetched by following the self link of a relationship object in the links object of a result. For the posts and comments example you could use the following generated URL:

GET /v1/posts/1/relationships/comments

This would return all comments that are currently referenced by post with ID 1. For example:

{
  "links": {
    "self": "/v1/posts/1/relationships/comments",
    "related": "/v1/posts/1/comments"
  },
  "data": [
    {
      "type": "comments",
      "id": "1"
    },
    {
      "type":"comments",
      "id": "2"
    }
  ]
}

Api2go always creates a related field for elements in the relationships object of the result. This is like it's specified on jsonapi.org. Post example:

{
  "data": [
    {
      "id": "1",
      "type": "posts",
      "title": "Foobar",
      "relationships": {
        "comments": {
          "links": {
            "related": "/v1/posts/1/comments",
            "self": "/v1/posts/1/relationships/comments"
          },
          "data": [
            {
              "id": "1",
              "type": "comments"
            },
            {
              "id": "2",
              "type": "comments"
            }
          ]
        }
      }
    }
  ]
}

If a client requests this related url, the FindAll method of the comments resource will be called with a query parameter postsID.

So if you implement the FindAll method, do not forget to check for all possible query Parameters. This means you have to check all your other structs and if it references the one for that you are implementing FindAll, check for the query Paramter and only return comments that belong to it. In this example, return the comments for the Post.

Using middleware

We provide a custom APIContext with a context implementation that you can use if you for example need to check if a user is properly authenticated before a request reaches the api2go routes.

You can either use our struct or implement your own with the APIContexter interface

type APIContexter interface {
    context.Context
    Set(key string, value interface{})
    Get(key string) (interface{}, bool)
    Reset()
}

If you implemented your own APIContexter, don't forget to define a APIContextAllocatorFunc and set it with func (api *API) SetContextAllocator(allocator APIContextAllocatorFunc)

But in most cases, this is not needed.

To use a middleware, it is needed to implement our type HandlerFunc func(APIContexter, http.ResponseWriter, *http.Request). A HandlerFunc can then be registered with func (api *API) UseMiddleware(middleware ...HandlerFunc). You can either pass one or many middlewares that will be executed in order before any other api2go routes. Use this to set up database connections, user authentication and so on.

Dynamic URL handling

If you have different TLDs for one api, or want to use different domains in development and production, you can implement a custom URLResolver in api2go.

There is a simple interface, which can be used if you get TLD information from the database, the server environment, or anything else that's not request dependant:

type URLResolver interface {
	GetBaseURL() string
}

And a more complex one that also gets request information:

type RequestAwareURLResolver interface {
	URLResolver
	SetRequest(http.Request)
}

For most use cases we provide a CallbackResolver which works on a per request basis and may fill your basic needs. This is particulary useful if you are using an nginx proxy which sets X-Forwarded-For headers.

resolver := NewCallbackResolver(func(r http.Request) string{})
api := NewApiWithMarshalling("v1", resolver, marshalers)

Tests

go test ./...
ginkgo -r                # Alternative
ginkgo watch -r -notify  # Watch for changes

Documentation

Overview

Package api2go enables building REST servers for the JSONAPI.org standard.

See https://github.com/artpar/api2go for usage instructions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ContextQueryParams

func ContextQueryParams(c *APIContext) map[string][]string

ContextQueryParams fetches the QueryParams if Set

func EndsWith

func EndsWith(str string, endsWith string) (string, bool)

func EndsWithCheck

func EndsWithCheck(str string, endsWith string) bool

Types

type API

type API struct {
	ContentType string
	// contains filtered or unexported fields
}

API is a REST JSONAPI.

func NewAPI

func NewAPI(prefix string) *API

NewAPI returns an initialized API instance `prefix` is added in front of all endpoints.

func NewAPIWithBaseURL

func NewAPIWithBaseURL(prefix string, baseURL string) *API

NewAPIWithBaseURL does the same as NewAPI with the addition of a baseURL which get's added in front of all generated URLs. For example http://localhost/v1/myResource/abc instead of /v1/myResource/abc

func NewAPIWithResolver

func NewAPIWithResolver(prefix string, resolver URLResolver) *API

NewAPIWithResolver can be used to create an API with a custom URL resolver.

func NewAPIWithRouting

func NewAPIWithRouting(prefix string, resolver URLResolver, router routing.Routeable) *API

NewAPIWithRouting allows you to use a custom URLResolver, marshalers and custom routing if you want to use the default routing, you should use another constructor.

If you don't need any of the parameters you can skip them with the defaults: the default for `prefix` would be `""`, which means there is no namespace for your api. although we suggest using one.

if your api only answers to one url you can use a NewStaticResolver() as `resolver`

func (*API) AddResource

func (api *API) AddResource(prototype jsonapi.MarshalIdentifier, source interface{})

AddResource registers a data source for the given resource At least the CRUD interface must be implemented, all the other interfaces are optional. `resource` should be either an empty struct instance such as `Post{}` or a pointer to a struct such as `&Post{}`. The same type will be used for constructing new elements.

func (API) Handler

func (api API) Handler() http.Handler

Handler returns the http.Handler instance for the API.

func (*API) NewAPIVersion

func (api *API) NewAPIVersion(prefix string) *API

NewAPIVersion can be used to chain an additional API version to the routing of a previous one. Use this if you have multiple version prefixes and want to combine all your different API versions. This reuses the baseURL or URLResolver

func (API) Router

func (api API) Router() routing.Routeable

Router returns the specified router on an api instance

func (*API) SetContextAllocator

func (api *API) SetContextAllocator(allocator APIContextAllocatorFunc)

SetContextAllocator custom implementation for making contexts

func (*API) UseMiddleware

func (api *API) UseMiddleware(middleware ...HandlerFunc)

UseMiddleware registers middlewares that implement the api2go.HandlerFunc Middleware is run before any generated routes.

type APIContext

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

APIContext api2go context for handlers, nil implementations related to Deadline and Done.

func (*APIContext) Deadline

func (c *APIContext) Deadline() (deadline time.Time, ok bool)

Deadline implements net/context

func (*APIContext) Done

func (c *APIContext) Done() <-chan struct{}

Done implements net/context

func (*APIContext) Err

func (c *APIContext) Err() error

Err implements net/context

func (*APIContext) Get

func (c *APIContext) Get(key string) (value interface{}, exists bool)

Get a key value from the context

func (*APIContext) Reset

func (c *APIContext) Reset()

Reset resets all values on Context, making it safe to reuse

func (*APIContext) Set

func (c *APIContext) Set(key string, value interface{})

Set a string key value in the context

func (*APIContext) Value

func (c *APIContext) Value(key interface{}) interface{}

Value implements net/context

type APIContextAllocatorFunc

type APIContextAllocatorFunc func(*API) APIContexter

APIContextAllocatorFunc to allow custom context implementations

type APIContexter

type APIContexter interface {
	context.Context
	Set(key string, value interface{})
	Get(key string) (interface{}, bool)
	Reset()
}

APIContexter embedding context.Context and requesting two helper functions

type Api2GoModel

type Api2GoModel struct {
	DeleteIncludes map[string][]string

	Includes []jsonapi.MarshalIdentifier
	// contains filtered or unexported fields
}

func NewApi2GoModel

func NewApi2GoModel(name string, columns []ColumnInfo, defaultPermission int64, relations []TableRelation) Api2GoModel

func NewApi2GoModelWithData

func NewApi2GoModelWithData(
	name string,
	columns []ColumnInfo,
	defaultPermission int64,
	relations []TableRelation,
	m map[string]interface{},
) Api2GoModel

func (*Api2GoModel) AddToManyIDs

func (m *Api2GoModel) AddToManyIDs(name string, IDs []string) error

func (*Api2GoModel) BeforeCreate

func (g *Api2GoModel) BeforeCreate() (err error)

func (*Api2GoModel) DeleteToManyIDs

func (m *Api2GoModel) DeleteToManyIDs(name string, IDs []string) error

func (Api2GoModel) GetAllAsAttributes

func (m Api2GoModel) GetAllAsAttributes() map[string]interface{}

func (Api2GoModel) GetAttributes

func (m Api2GoModel) GetAttributes() map[string]interface{}

func (Api2GoModel) GetAuditModel

func (g Api2GoModel) GetAuditModel() Api2GoModel

func (Api2GoModel) GetChanges

func (g Api2GoModel) GetChanges() map[string]Change

func (Api2GoModel) GetColumnMap

func (a Api2GoModel) GetColumnMap() map[string]ColumnInfo

func (Api2GoModel) GetColumnNames

func (m Api2GoModel) GetColumnNames() []string

func (Api2GoModel) GetColumnOriginalValue

func (g Api2GoModel) GetColumnOriginalValue(columnName string) interface{}

func (Api2GoModel) GetColumns

func (m Api2GoModel) GetColumns() []ColumnInfo

func (Api2GoModel) GetCurrentVersion

func (g Api2GoModel) GetCurrentVersion() int64

func (Api2GoModel) GetDefaultPermission

func (g Api2GoModel) GetDefaultPermission() int64

func (Api2GoModel) GetID

func (g Api2GoModel) GetID() string

func (Api2GoModel) GetName

func (g Api2GoModel) GetName() string

func (Api2GoModel) GetNextVersion

func (g Api2GoModel) GetNextVersion() int64

func (Api2GoModel) GetReferenceId

func (g Api2GoModel) GetReferenceId() string

func (Api2GoModel) GetReferencedIDs

func (m Api2GoModel) GetReferencedIDs() []jsonapi.ReferenceID

func (Api2GoModel) GetReferencedStructs

func (m Api2GoModel) GetReferencedStructs() []jsonapi.MarshalIdentifier

func (Api2GoModel) GetReferences

func (model Api2GoModel) GetReferences() []jsonapi.Reference

func (Api2GoModel) GetRelations

func (a Api2GoModel) GetRelations() []TableRelation

func (Api2GoModel) GetTableName

func (g Api2GoModel) GetTableName() string

func (Api2GoModel) GetUnmodifiedAttributes

func (g Api2GoModel) GetUnmodifiedAttributes() map[string]interface{}

func (Api2GoModel) HasColumn

func (a Api2GoModel) HasColumn(colName string) bool

func (Api2GoModel) HasMany

func (a Api2GoModel) HasMany(colName string) bool

func (Api2GoModel) HasVersion

func (g Api2GoModel) HasVersion() bool

func (Api2GoModel) InitializeObject

func (m Api2GoModel) InitializeObject(interface{})

func (Api2GoModel) IsDirty

func (g Api2GoModel) IsDirty() bool

func (*Api2GoModel) Set

func (g *Api2GoModel) Set(columnName string, value interface{})

func (*Api2GoModel) SetAttributes

func (g *Api2GoModel) SetAttributes(attrs map[string]interface{})

func (*Api2GoModel) SetColumns

func (m *Api2GoModel) SetColumns(c []ColumnInfo)

func (*Api2GoModel) SetID

func (g *Api2GoModel) SetID(str string) error

func (*Api2GoModel) SetToManyReferenceIDs

func (m *Api2GoModel) SetToManyReferenceIDs(name string, IDs []map[string]interface{}) error

func (Api2GoModel) SetToOneReferenceID

func (m Api2GoModel) SetToOneReferenceID(name, ID string) error

func (*Api2GoModel) SetType

func (g *Api2GoModel) SetType(typeName string)

type CRUD

The CRUD interface embed all interfaces at once: `ResourceCreator`, `ResourceDeleter`, `ResourceUpdater` (which includes `ResourceGetter`)

type Change

type Change struct {
	OldValue interface{}
	NewValue interface{}
}

type ColumnInfo

type ColumnInfo struct {
	Name              string         `db:"name"`
	ColumnName        string         `db:"column_name"`
	ColumnDescription string         `db:"column_description"`
	ColumnType        string         `db:"column_type"`
	IsPrimaryKey      bool           `db:"is_primary_key"`
	IsAutoIncrement   bool           `db:"is_auto_increment"`
	IsIndexed         bool           `db:"is_indexed"`
	IsUnique          bool           `db:"is_unique"`
	IsNullable        bool           `db:"is_nullable"`
	Permission        uint64         `db:"permission"`
	IsForeignKey      bool           `db:"is_foreign_key"`
	ExcludeFromApi    bool           `db:"include_in_api"`
	ForeignKeyData    ForeignKeyData `db:"foreign_key_data"`
	DataType          string         `db:"data_type"`
	DefaultValue      string         `db:"default_value"`
	Options           []ValueOptions
}

type DeleteReferenceInfo

type DeleteReferenceInfo struct {
	ReferenceRelationName string
	ReferenceId           string
}

type EditToManyRelations

type EditToManyRelations interface {
	AddToManyIDs(name string, IDs []string) error
	DeleteToManyIDs(name string, IDs []string) error
}

The EditToManyRelations interface can be optionally implemented to add and delete to-many relationships on a already unmarshalled struct. These methods are used by our API for the to-many relationship update routes.

There are 3 HTTP Methods to edit to-many relations:

PATCH /v1/posts/1/comments
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": [
	{ "type": "comments", "id": "2" },
	{ "type": "comments", "id": "3" }
  ]
}

This replaces all of the comments that belong to post with ID 1 and the SetToManyReferenceIDs method will be called.

POST /v1/posts/1/comments
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": [
	{ "type": "comments", "id": "123" }
  ]
}

Adds a new comment to the post with ID 1. The AddToManyIDs method will be called.

DELETE /v1/posts/1/comments
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": [
	{ "type": "comments", "id": "12" },
	{ "type": "comments", "id": "13" }
  ]
}

Deletes comments that belong to post with ID 1. The DeleteToManyIDs method will be called.

type Error

type Error struct {
	ID     string       `json:"id,omitempty"`
	Links  *ErrorLinks  `json:"links,omitempty"`
	Status string       `json:"status,omitempty"`
	Code   string       `json:"code,omitempty"`
	Title  string       `json:"title,omitempty"`
	Detail string       `json:"detail,omitempty"`
	Source *ErrorSource `json:"source,omitempty"`
	Meta   interface{}  `json:"meta,omitempty"`
}

Error can be used for all kind of application errors e.g. you would use it to define form errors or any other semantical application problems for more information see http://jsonapi.org/format/#errors

type ErrorLinks struct {
	About string `json:"about,omitempty"`
}

ErrorLinks is used to provide an About URL that leads to further details about the particular occurrence of the problem.

for more information see http://jsonapi.org/format/#error-objects

type ErrorSource

type ErrorSource struct {
	Pointer   string `json:"pointer,omitempty"`
	Parameter string `json:"parameter,omitempty"`
}

ErrorSource is used to provide references to the source of an error.

The Pointer is a JSON Pointer to the associated entity in the request document. The Paramter is a string indicating which query parameter caused the error.

for more information see http://jsonapi.org/format/#error-objects

type FindAll

type FindAll interface {
	// FindAll returns all objects
	FindAll(req Request) (Responder, error)
}

The FindAll interface can be optionally implemented to fetch all records at once.

type ForeignKeyData

type ForeignKeyData struct {
	DataSource string
	Namespace  string
	KeyName    string
}

func (*ForeignKeyData) Scan

func (f *ForeignKeyData) Scan(src interface{}) error

Parse format "namespace:tableName(column)"

func (ForeignKeyData) String

func (f ForeignKeyData) String() string

type HTTPError

type HTTPError struct {
	Errors []Error `json:"errors,omitempty"`
	// contains filtered or unexported fields
}

HTTPError is used for errors

func NewHTTPError

func NewHTTPError(err error, msg string, status int) HTTPError

NewHTTPError creates a new error with message and status code. `err` will be logged (but never sent to a client), `msg` will be sent and `status` is the http status code. `err` can be nil.

func (HTTPError) Error

func (e HTTPError) Error() string

Error returns a nice string represenation including the status

func (HTTPError) Status

func (e HTTPError) Status() int

Error returns a nice string represenation including the status

type HandlerFunc

type HandlerFunc func(APIContexter, http.ResponseWriter, *http.Request)

HandlerFunc for api2go middlewares

type HasId

type HasId interface {
	GetId() interface{}
}

type LinksResponder

type LinksResponder interface {
	Responder
	Links(*http.Request, string) jsonapi.Links
}

The LinksResponder interface may be used when the response object is able to return a set of links for the top-level response object.

type ObjectInitializer

type ObjectInitializer interface {
	InitializeObject(interface{})
}

The ObjectInitializer interface can be implemented to have the ability to change a created object before Unmarshal is called. This is currently only called on Create as the other actions go through FindOne or FindAll which are already controlled by the implementer.

type PaginatedFindAll

type PaginatedFindAll interface {
	PaginatedFindAll(req Request) (totalCount uint, response Responder, err error)
}

The PaginatedFindAll interface can be optionally implemented to fetch a subset of all records. Pagination query parameters must be used to limit the result. Pagination URLs will automatically be generated by the api. You can use a combination of the following 2 query parameters: page[number] AND page[size] OR page[offset] AND page[limit]

type Pagination

type Pagination struct {
	Next        map[string]string
	Prev        map[string]string
	First       map[string]string
	Last        map[string]string
	Total       uint64
	PerPage     uint64
	CurrentPage uint64
	LastPage    uint64
	NextPageUrl string
	PrevPageUrl string
	From        uint64
	To          uint64
}

Pagination represents information needed to return pagination links

type Request

type Request struct {
	PlainRequest *http.Request
	QueryParams  map[string][]string
	Pagination   map[string]string
	Header       http.Header
	Context      APIContexter
}

Request contains additional information for FindOne and Find Requests

type RequestAwareURLResolver

type RequestAwareURLResolver interface {
	URLResolver
	SetRequest(http.Request)
}

RequestAwareURLResolver allows you to dynamically change generated urls.

This is particulary useful if you have the same API answering to multiple domains, or subdomains e.g customer[1,2,3,4].yourapi.example.com

SetRequest will always be called prior to the GetBaseURL() from `URLResolver` so you have to change the result value based on the last request.

type ResourceCreator

type ResourceCreator interface {
	//Create a new object. Newly created object/struct must be in Responder.
	// Possible Responder status codes are:
	// - 201 Created: Resource was created and needs to be returned
	// - 202 Accepted: Processing is delayed, return nothing
	// - 204 No Content: Resource created with a client generated ID, and no fields were modified by
	//   the server
	Create(obj interface{}, req Request) (Responder, error)
}

The ResourceCreator interface MUST be implemented in order to generate the POST route

type ResourceDeleter

type ResourceDeleter interface {
	//Delete an object
	// Possible Responder status codes are:
	// - 200 OK: Deletion was a success, returns meta information, currently not implemented! Do not use this
	// - 202 Accepted: Processing is delayed, return nothing
	// - 204 No Content: Deletion was successful, return nothing
	Delete(id string, req Request) (Responder, error)
}

The ResourceDeleter interface MUST be implemented in order to generate the DELETE route

type ResourceGetter

type ResourceGetter interface {
	// FindOne returns an object by its ID
	// Possible Responder success status code 200
	FindOne(ID string, req Request) (Responder, error)
}

The ResourceGetter interface MUST be implemented in order to generate the single GET route and related

type ResourceUpdater

type ResourceUpdater interface {
	// ResourceGetter must be implemented along with ResourceUpdater so that api2go can retrieve the single resource before update
	ResourceGetter
	//Update an object
	// Possible Responder status codes are:
	// - 200 OK: Update successful, however some field(s) were changed, returns updates source
	// - 202 Accepted: Processing is delayed, return nothing
	// - 204 No Content: Update was successful, no fields were changed by the server, return nothing
	Update(obj interface{}, req Request) (Responder, error)
}

The ResourceUpdater interface MUST be implemented in order to generate the PATCH/PUT routes

type Responder

type Responder interface {
	Metadata() map[string]interface{}
	Result() interface{}
	StatusCode() int
}

The Responder interface is used by all Resource Methods as a container for the Response. Metadata is additional Metadata. You can put anything you like into it, see jsonapi spec. Result returns the actual payload. For FindOne, put only one entry in it. StatusCode sets the http status code.

type Response

type Response struct {
	Res        interface{}
	Code       int
	Meta       map[string]interface{}
	Pagination Pagination
}

The Response struct implements api2go.Responder and can be used as a default implementation for your responses you can fill the field `Meta` with all the metadata your application needs like license, tokens, etc

func (r Response) Links(req *http.Request, baseURL string) (ret jsonapi.Links)

Links returns a jsonapi.Links object to include in the top-level response

func (Response) Metadata

func (r Response) Metadata() map[string]interface{}

Metadata returns additional meta data

func (Response) Result

func (r Response) Result() interface{}

Result returns the actual payload

func (Response) StatusCode

func (r Response) StatusCode() int

StatusCode sets the return status code

type TableRelation

type TableRelation struct {
	Subject     string
	Object      string
	Relation    string
	SubjectName string
	ObjectName  string
	Columns     []ColumnInfo
}

func NewTableRelation

func NewTableRelation(subject, relation, object string) TableRelation

func NewTableRelationWithNames

func NewTableRelationWithNames(subject, subjectName, relation, object, objectName string) TableRelation

func (*TableRelation) GetJoinString

func (tr *TableRelation) GetJoinString() string

func (*TableRelation) GetJoinTableName

func (tr *TableRelation) GetJoinTableName() string

func (*TableRelation) GetObject

func (tr *TableRelation) GetObject() string

func (*TableRelation) GetObjectName

func (tr *TableRelation) GetObjectName() string

func (*TableRelation) GetRelation

func (tr *TableRelation) GetRelation() string

func (*TableRelation) GetReverseJoinString

func (tr *TableRelation) GetReverseJoinString() string

func (*TableRelation) GetSubject

func (tr *TableRelation) GetSubject() string

func (*TableRelation) GetSubjectName

func (tr *TableRelation) GetSubjectName() string

func (*TableRelation) Hash

func (tr *TableRelation) Hash() string

func (*TableRelation) String

func (tr *TableRelation) String() string

type TableRelationInterface

type TableRelationInterface interface {
	GetSubjectName() string
	GetRelation() string
	GetObjectName() string
}

type URLResolver

type URLResolver interface {
	GetBaseURL() string
}

URLResolver allows you to implement a static way to return a baseURL for all incoming requests for one api2go instance.

func NewCallbackResolver

func NewCallbackResolver(callback func(http.Request) string) URLResolver

NewCallbackResolver handles each resolve via your provided callback func

func NewStaticResolver

func NewStaticResolver(baseURL string) URLResolver

NewStaticResolver returns a simple resolver that will always answer with the same url

type ValueOptions

type ValueOptions struct {
	ValueType string
	Value     interface{}
	Label     string
}

Directories

Path Synopsis
Package examples shows how to implement a basic CRUD for two data structures with the api2go server functionality.
Package examples shows how to implement a basic CRUD for two data structures with the api2go server functionality.

Jump to

Keyboard shortcuts

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