apicalypse

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Feb 5, 2020 License: MIT Imports: 5 Imported by: 9

README

Apicalypse

GoDoc Build Status Coverage Status Go Report Card

Create custom Apicalypse compliant queries with the apicalypse package. With the apicalypse package, you can create queries with any of the supported filters including: Fields, Exclude, Where, Limit, Offset, Sort, and Search. The package will be kept up to date with Apicalypse - if more filters are introduced, they will be supported here as well. To see the complete list of filters and their respective syntax please visit the Apicalypse syntax page here.

If you would like to lend a hand with the package, please feel free to submit a pull request. Any and all contributions are welcome and appreciated!

If you landed here but were looking for the IGDB package, you can find that here.

Installation

If you do not have Go installed yet, you can find installation instructions here.

To pull the most recent version of apicalypse, use go get.

go get github.com/Henry-Sarabia/apicalypse

Then import the package into your project as you normally would.

import "github.com/Henry-Sarabia/apicalypse"

Usage

Creating A New Query

Creating a new Apicalypse query is simple - use the provided Query() function along with whatever filters you need. In our example, we need a query that will limit the number of results to 25 and retrieve an object's "name" and "age" fields.

qry, err := apicalypse.Query(Limit(25), Fields("name", "age"))
if err != nil {
	// handle error
}

With that, we have a query ready to be sent to your favorite Apicalypse-powered API. This is sent primarily in the body of a GET request. Of course, this package makes that easy.

Creating A New Request

The apicalypse package also provides a convenient NewRequest() function to get you up and running. In this example, we need a request fulfulling the same requirements as the previous example.

req, err := apicalypse.NewRequest(
	"GET", 
	"https://myapi.com/actors", 
	Limit(25), 
	Fields("name", "age"),
	)
if err != nil {
	// handle error
}

Just like that, we have a request configured with the necessary filters. If we had any headers that need their own configuration, this would be the time to do it. If not, the request can be sent off straight away.

You may provide any method and URL to the function but be aware that the Apicalypse specifications recommend a simple GET request for the majority of cases. POST and PUT requests may be used under certain circumstances.

For more information, please visit the Apicalypse implementation page here.

Functional Options

The apicalypse package uses functional options to apply the different query filters to a request. More specifically, functional options are first-order functions that are passed to the NewRequest() function. It's much simpler than it sounds and the package makes it easy!

Let's walk through a few different functional option examples to demonstrate their ease of use.

To specify what fields to return from an API query, pass Fields() to the NewRequest() function.

req, err := apicalypse.NewRequest("GET", "https://myapi.com/actors", Fields("name", "movies", "age"))

With that, our new request is configured to only fetch the "name", "movies", and "age" fields from our friends at the totally, absolutely real myapi.com.

To specify the limit of results we want returned from an API query, pass Limit() to the NewRequest() function.

req, err := apicalypse.NewRequest("GET", "https://myapi.com/actors", Limit(15))

It's no more difficult than that - we're ready to fetch up to 15 results!

Although this next example may initially look more complicated, it is only a matter of adhering to the proper Apicalypse syntax. Again, that can be found here.

To specify a filter for our results from an API query, pass Where() to the NewRequest() function.

req, err := apicalypse.NewRequest("GET", "https://myapi.com/actors", Where("age > 50 & movies != null"))

Our new request is now configured to filter the results so only the results which have an age above 50 and a non-null movies field are returned.

The remaining functional options are no more complicated than the examples presented here. Moreover, they are further described in the documentation.

Functional Option Composition

More often than not you will need to set multiple options for an API query. Fortunately, this functionality is supported through variadic functions and functional option composition.

First, the NewRequest() and Query() functions are variadic which means you can pass in any number of functional options.

req, err := apicalypse.NewRequest(
	"GET",
	"https://myapi.com/actors",
	Fields("name", "movies", "age"),
	Where("age > 50 & movies != null"),
	Limit(15),
	)

Leveraging the variadic nature of our primary functions, we have quickly and simply created a request with several different filters automatically applied to it.

This request is now configured to return results with the fields "name", "movies", and "age". In addition, the results will be filtered so only results with age above 50 and a non-null movies field are returned. Finally, only up to 15 results will be returned.

Second, the apicalypse package provides a ComposeOptions() function which takes any number of functional options and composes them into a single, custom made, ready-to-use functional option.

myOpt := apicalypse.ComposeOptions(
	Fields("name", "movies", "age"),
        Where("age > 50 & movies != null"),
        Limit(15),
	)

This call to ComposeOptions() creates a single functional option that performs the same filters we've been using up until now. The major difference is that now we only need to provide this new single functional option to any new requests that require those specific filters.

req, err := apicalypse.NewRequest("GET", "https://myapi.com/actors", myOpt)

Of course, you can still pass in additional functional options if need be.

For example, now that we've gathered our 15 results, perhaps we want the next 15. It's as simple as passing in one more functional option.

req, err := apicalypse.NewRequest("GET", "https://myapi.com/actors", myOpt, Offset(15))

Our new request is ready to return the next 15 results!

Functional option composition reduces duplicate code and helps keep your code DRY. You can even compose newly composed functional options for even more finely grained control over similar queries.

Examples

The repository contains a few examples that demonstrate how one could use the apicalypse package. The examples can be found in their respective test files or be read conveniently on the GoDoc reference found here.

If you have used the apicalypse package for a project and would like to have it featured here as a reference for new users, please submit an issue or pull request and I'll be sure to add it. Thank you!

Contributions

If you would like to contribute to this project, please adhere to the following guidelines.

  • Submit an issue describing the problem.
  • Fork the repo and add your contribution.
  • Add appropriate tests.
  • Run go fmt, go vet, and golint.
  • Prefer idiomatic Go over non-idiomatic code.
  • Follow the basic Go conventions found here.
  • If in doubt, try to match your code to the current codebase.
  • Create a pull request with a description of your changes.

I'll review pull requests as they come in and merge them if everything checks out.

Again, any contribution is greatly appreciated!

Special Thanks

  • The IGDB team who developed Apicalypse with an accompanying Node client
  • The helpful community over on the IGDB Discord server
  • The insightful Dave Cheney for his article on functional options

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrMissingInput occurs when a function is called without input parameters (e.g. nil slice)
	ErrMissingInput = errors.New("missing input parameters")
	// ErrBlankArgument occurs when a function is called with a blank argument that should not be blank.
	ErrBlankArgument = errors.New("a provided argument is blank or empty")
	// ErrNegativeInput occurs when a function is called with a negative number that should not be negative.
	ErrNegativeInput = errors.New("input cannot be a negative number")
)

Functions

func NewRequest

func NewRequest(method string, url string, opts ...Option) (*http.Request, error)

NewRequest returns a request configured for the provided url using the provided method. The provided query options are written to the body of the request. The default method is GET.

func Query

func Query(opts ...Option) (string, error)

Query processes the provided functional options into an Apicalypse compliant format and returns it as a string. The string is ready to be written into the body of an HTTP Request.

Types

type Option

type Option func(map[string]string) error

Option is a functional option type used to set the filters for an API query. Option is the first-order function returned by the available functional options (e.g. Fields or Limit). For the full list of supported filters and their expected syntax, please visit: https://apicalypse.io/syntax/

func ComposeOptions

func ComposeOptions(opts ...Option) Option

ComposeOptions composes multiple functional options into a single Option. This is primarily used to create a single functional option that can be used repeatedly across multiple queries.

Example
// Composing FuncOptions to filter out unpopular results
composedOpts := ComposeOptions(
	Fields("title", "username", "game", "likes", "content"),
	Where("likes > 10"),
	Where("views >= 200"),
	Limit(25),
)

// Using composed FuncOptions
req1, err := NewRequest("GET", "https://some-internet-game-database-api/games/", composedOpts)
if err != nil {
	fmt.Println(err)
	return
}

// Reusing composed FuncOptions
req2, err := NewRequest("GET", "https://some-internet-game-database-api/games/", composedOpts, Offset(25))
if err != nil {
	fmt.Println(err)
	return
}

// Retrieves first set of 15 popular games
http.DefaultClient.Do(req1)
// Retrieves second set of 15 popular games
http.DefaultClient.Do(req2)
Output:

func Exclude

func Exclude(fields ...string) Option

Exclude is a functional option for setting the excluded fields in the results from a query.

Example
// Retrieve games without name field
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("name"))

// Retrieve games without genres field
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("genres"))

// Retrieve games without name or genres field
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("name", "genres"))

// Retrieve games without parent game
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("parent_game"))

// Retrieve games without parent game name
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("parent_game.name"))

// Retrieve games without any number of fields
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Exclude("name", "genres", "popularity", "rating"))

// Execute latest request
http.DefaultClient.Do(req)
Output:

func Fields

func Fields(fields ...string) Option

Fields is a functional option for setting the included fields in the results from a query.

Example
// Retrieve games with name field
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("name"))

// Retrieve games with genres field
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("genres"))

// Retrieve games with both name and genres field
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("name", "genres"))

// Retrieve games with a parent game
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("parent_game"))

// Retrieve games with a parent game name
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("parent_game.name"))

// Retrieve games with any number of fields
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("name", "genres", "popularity", "rating"))

// Retrieve games with all available fields
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Fields("*"))

// Execute latest request
http.DefaultClient.Do(req)
Output:

func Limit

func Limit(n int) Option

Limit is a functional option for setting the number of items to return from a query. This usually has a maximum limit.

Example
// Retrieve up to 1 result
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(1))

// Retrieve up to 25 results
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(25))

// Retrieve up to 50 results
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(50))

// Execute latest request
http.DefaultClient.Do(req)
Output:

func Offset

func Offset(n int) Option

Offset is a functional option for setting the index to start returning results from a query.

Example
// Retrieve the first batch of 10 results
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(10))

// Retrieve the second batch of 10 results
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(10), Offset(10))

// Retrieve the third batch of 10 results
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(10), Offset(20))

// Retrieve the fourth batch of 10 results
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Limit(10), Offset(30))

http.DefaultClient.Do(req)
Output:

func Search(column, term string) Option

Search is a functional option for searching for a value in a particular column of data. If the column is omitted, search will be performed on the default column.

func Sort

func Sort(field, order string) Option

Sort is a functional option for sorting the results of a query by a certain field's values and the use of "asc" or "desc" to sort by ascending or descending order.

Example
// Retrieve the most popular games
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Sort("popularity", "desc"))

// Retrieve the least popular games
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Sort("popularity", "asc"))

// Retrieve the earliest released games by their first release date
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Sort("first_release_date", "asc"))

// Retrieve games with the latest release date
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Sort("first_release_date", "desc"))

http.DefaultClient.Do(req)
Output:

func Where

func Where(custom ...string) Option

Where is a functional option for setting a custom data filter similar to SQL. If multiple filters are provided, they are AND'd together. For the full list of filters and more information, visit: https://apicalypse.io/syntax/

Example
// Retrieve games with a rating equal to 50
req, _ := NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating = 50"))

// Retrieve games with a rating not equal to 50
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating != 50"))

// Retrieve games with a rating greater than 50
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating > 50"))

// Retrieve games with a rating greater than or equal to 50
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating >= 50"))

// Retrieve games with a rating less than 50
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating < 50"))

// Retrieve games with a rating less than or equal to 50
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating <= 50"))

// Retrieve games with all of the following genres: Roleplaying, Adventure, and MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("genres = [Roleplaying, Adventure, MMO]"))

// Retrieve games without all of the following genres: genres Roleplaying, Adventure, and MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("genres != [Roleplaying, Adventure, MMO]"))

// Retrieve games with at least one of the following genres: Roleplaying, Adventure, or MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("genres = (Roleplaying, Adventure, MMO)"))

// Retrieve games without any of the following genres: Roleplaying, Adventure, or MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("genres != (Roleplaying, Adventure, MMO)"))

// Retrieve games with exclusively the following genres: Roleplaying, Adventure, or MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("genres = {Roleplaying, Adventure, MMO}"))

// Retrieve games with a rating greater than 50 AND with at least one of the following genres: Roleplaying, Adventure, or MMO
req, _ = NewRequest("GET", "https://some-internet-game-database-api/games/", Where("rating > 50", "genres = (Roleplaying, Adventure, MMO)"))

// Execute latest request
http.DefaultClient.Do(req)
Output:

Jump to

Keyboard shortcuts

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