apitoolkit

package module
v0.0.0-...-a357008 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2024 License: MIT Imports: 27 Imported by: 3

README

GoDoc

API Toolkit Golang Client

The API Toolkit golang client is an sdk used to integrate golang web services with APIToolkit. It monitors incoming traffic, gathers the requests and sends the request to the apitoolkit servers.

Design decisions:

  • Use the gcp SDK to send real time traffic from REST APIs to the gcp topic

How to Integrate:

First install the apitoolkit Go sdk: go get github.com/apitoolkit/apitoolkit-go

Gin web server integration

Then add apitoolkit to your app like so (Gin example):

package main

import (
  	// Import the apitoolkit golang sdk
  	apitoolkit "github.com/apitoolkit/apitoolkit-go"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
    		panic(err)
	}

  	router := gin.New()

  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.GinMiddleware)

  	// Register your handlers as usual and run the gin server as usual.
  	router.POST("/:slug/test", func(c *gin.Context) {c.Text(200, "ok")})
 	...
}

Fiber Router server integration
package main

import (
  	// Import the apitoolkit golang sdk
    apitoolkit "github.com/apitoolkit/apitoolkit-go"
    fiber "github.com/gofiber/fiber/v2"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
    		panic(err)
	}

  	router := fiber.New()

  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.FiberMiddleware)

  	// Register your handlers as usual and run the gin server as usual.
  	router.Post("/:slug/test", func(c *fiber.Ctx) {c.Status(200).JSON({"status":"ok"})})
 	...
}
Echo Router server integration
package main

import (
  	// Import the apitoolkit golang sdk
    apitoolkit "github.com/apitoolkit/apitoolkit-go"
    echo "github.com/labstack/echo/v4"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
    		panic(err)
	}

  	router := echo.New()

  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.EchoMiddleware)

  	// Register your handlers as usual and run the gin server as usual.
  	router.POST("/:slug/test", func(c *fiber.Ctx) {c.JSON(200, {"status":"ok"})})
 	...
}
Gorilla Mux Golang HTTP router integration
import (
  	// Import the apitoolkit golang sdk
    apitoolkit "github.com/apitoolkit/apitoolkit-go"
    "github.com/gorilla/mux"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
        panic(err)
	}

  	router := mux.NewRouter()
  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.GorillaMuxMiddleware)

  	// Register your handlers as usual and run the gin server as usual.
  	router.HandleFunc("/{slug:[a-z]+}/test", func(w http.ResponseWriter, r *http.Request) {..}).Methods(http.MethodPost)
 	...
}
Golang Chi HTTP router integration
import (
    apitoolkit "github.com/apitoolkit/apitoolkit-go"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
        panic(err)
	}

  	router := chi.NewRouter()
  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.ChiMiddleware)

  	// Register your handlers as usual and run the gin server as usual.
  	router.Post("/{slug:[a-z]+}/test", func(w http.ResponseWriter, r *http.Request) {..})
 	...
}
Native Golang HTTP router integration

Only use this as a last resort. Make a request via github issues if your routing library of choice is not supported.

package main

import (
  	// Import the apitoolkit golang sdk
    apitoolkit "github.com/apitoolkit/apitoolkit-go"
    "github.com/gorilla/mux"
)

func main() {
  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	if err != nil {
    		panic(err)
	}

  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.Middleware)

 	...
}

Client Redacting fields

While it's possible to mark a field as redacted from the apitoolkit dashboard, this client also supports redacting at the client side. Client side redacting means that those fields would never leave your servers at all. So you feel safer that your sensitive data only stays on your servers.

To mark fields that should be redacted, simply add them to the apitoolkit config struct. Eg:

func main() {
    	apitoolkitCfg := apitoolkit.Config{
        	RedactHeaders: []string{"Content-Type", "Authorization", "Cookies"},
        	RedactRequestBody: []string{"$.credit-card.cvv", "$.credit-card.name"},
        	RedactResponseBody: []string{"$.message.error"},
        	APIKey: "<APIKEY>",
    	}

  	// Initialize the client using your apitoolkit.io generated apikey
  	apitoolkitClient, _ := apitoolkit.NewClient(context.Background(), apitoolkitCfg)

  	router := gin.New()
  	// Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
  	router.Use(apitoolkitClient.GinMiddleware)
  	// Register your handlers as usual and run the gin server as usual.
  	router.POST("/:slug/test", func(c *gin.Context) {c.Text(200, "ok")})
 	...
}

It is important to note that while the RedactHeaders config field accepts a list of headers(case insensitive), the RedactRequestBody and RedactResponseBody expect a list of JSONPath strings as arguments.

The choice of JSONPath was selected to allow you have great flexibility in descibing which fields within your responses are sensitive. Also note that these list of items to be redacted will be aplied to all endpoint requests and responses on your server. To learn more about jsonpath to help form your queries, please take a look at this cheatsheet: https://lzone.de/cheat-sheet/JSONPath

Outgoing Requests

Access an instrumented http client which will automatically monitor requests made to third parties. These outgoing requests can be monitored from the apitoolkit dashboard

    httpClient := apitoolkit.HTTPClient(ctx)
    httpClient.Post(..) // Use the client like any regular golang http client. 

You can redact fields using functional options to specify what fields to redact. eg.

    httpClient := apitoolkit.HTTPClient(ctx, apitoolkit.WithRedactHeaders("ABC", "$abc.xyz"))
    httpClient.Post(..) // Use the client like any regular golang http client. 

Report Errors

If you've used sentry, or bugsnag, or rollbar, then you're already familiar with this usecase. But you can report an error to apitoolkit. A difference, is that errors are always associated with a parent request, and helps you query and associate the errors which occured while serving a given customer request. To request errors to APIToolkit use call the ReportError method of apitoolkit not the client returned by apitoolkit.NewClient with the request context and the error to report Examples:

Native Go

    file, err := os.Open("non-existing-file.txt")
    if err != nil {
        // Report the error to apitoolkit
        // Ensure that the ctx is the context which is passed down from the handlers.
        apitoolkit.ReportError(ctx, err)
    }

Documentation

Overview

APIToolkit: The API Toolkit golang client is an sdk used to integrate golang web services with APIToolkit. It monitors incoming traffic, gathers the requests and sends the request to the apitoolkit servers.

APIToolkit go sdk can be used with most popular Golang routers off the box. And if your routing library of choice is not supported, feel free to leave an issue on github, or send in a pull request.

Here's how the SDK can be used with a gin server: ```go

func main(){
   // Initialize the client using your apitoolkit.io generated apikey
   apitoolkitClient, err := apitoolkit.NewClient(context.Background(), apitoolkit.Config{APIKey: "<APIKEY>"})
	 if err != nil {
    		panic(err)
	 }

   router := gin.New()

	 // Register with the corresponding middleware of your choice. For Gin router, we use the GinMiddleware method.
   router.Use(apitoolkitClient.GinMiddleware)

   // Register your handlers as usual and run the gin server as usual.
   router.POST("/:slug/test", func(c *gin.Context) {c.String(200, "ok")})
}

```

Index

Constants

View Source
const (
	GoDefaultSDKType = "GoBuiltIn"
	GoGinSDKType     = "GoGin"
	GoGorillaMux     = "GoGorillaMux"
	GoOutgoing       = "GoOutgoing"
	GoFiberSDKType   = "GoFiber"
)

Variables

View Source
var (
	ErrorListCtxKey         = ctxKey("error-list")
	CurrentRequestMessageID = ctxKey("current-req-msg-id")
	CurrentClient           = ctxKey("current=apitoolkit-client")
)

Functions

func HTTPClient

func HTTPClient(ctx context.Context, opts ...RoundTripperOption) *http.Client

func ReportError

func ReportError(ctx context.Context, err error)

ReportError Allows you to report an error from your server to APIToolkit. This error would be associated with a given request, and helps give a request more context especially when investigating incidents

Types

type ATError

type ATError struct {
	When             time.Time `json:"when,omitempty"`
	ErrorType        string    `json:"error_type,omitempty"`
	RootErrorType    string    `json:"root_error_type,omitempty"`
	Message          string    `json:"message,omitempty"`
	RootErrorMessage string    `json:"root_error_message,omitempty"`
	StackTrace       string    `json:"stack_trace,omitempty"`
}

ATError is the Apitoolkit error type/object

type Client

type Client struct {
	PublishMessage func(ctx context.Context, payload Payload) error
	// contains filtered or unexported fields
}

func NewClient

func NewClient(ctx context.Context, cfg Config) (*Client, error)

NewClient would initialize an APIToolkit client which we can use to push data to apitoolkit.

func (*Client) ChiMiddleware

func (c *Client) ChiMiddleware(next http.Handler) http.Handler

ChiMiddleware is for the Golang Chi router and collects request, response parameters and publishes the payload

func (*Client) Close

func (c *Client) Close() error

Close cleans up the apitoolkit client. It should be called before the app shorts down, ideally as a defer call.

func (*Client) EchoMiddleware

func (c *Client) EchoMiddleware(next echo.HandlerFunc) echo.HandlerFunc

EchoMiddleware middleware for echo framework, collects requests, response and publishes the payload

func (*Client) FiberMiddleware

func (c *Client) FiberMiddleware(ctx *fiber.Ctx) error

func (*Client) GinMiddleware

func (c *Client) GinMiddleware(ctx *gin.Context)

func (*Client) GorillaMuxMiddleware

func (c *Client) GorillaMuxMiddleware(next http.Handler) http.Handler

GorillaMuxMiddleware is for the gorilla mux routing library and collects request, response parameters and publishes the payload

func (*Client) Middleware

func (c *Client) Middleware(next http.Handler) http.Handler

Middleware collects request, response parameters and publishes the payload

func (*Client) WrapRoundTripper

func (c *Client) WrapRoundTripper(ctx context.Context, rt http.RoundTripper, opts ...RoundTripperOption) http.RoundTripper

WrapRoundTripper returns a new RoundTripper which traces all requests sent over the transport.

type ClientMetadata

type ClientMetadata struct {
	ProjectId                string          `json:"project_id"`
	PubsubProjectId          string          `json:"pubsub_project_id"`
	TopicID                  string          `json:"topic_id"`
	PubsubPushServiceAccount json.RawMessage `json:"pubsub_push_service_account"`
}

type Config

type Config struct {
	Debug bool
	// VerboseDebug should never be enabled in production
	// and logs entire message body which gets sent to APIToolkit
	VerboseDebug bool
	RootURL      string
	APIKey       string
	ProjectID    string
	// ServiceVersion is an identifier to help you track deployments. This could be a semver version or a git hash or anything you like.
	ServiceVersion string
	// A list of field headers whose values should never be sent to apitoolkit
	RedactHeaders      []string
	RedactRequestBody  []string
	RedactResponseBody []string
	// Tags are arbitrary identifiers for service being tracked, and can be used as filters on apitoolkit.
	Tags []string `json:"tags"`
}

type Payload

type Payload struct {
	Timestamp       time.Time           `json:"timestamp"`
	RequestHeaders  map[string][]string `json:"request_headers"`
	QueryParams     map[string][]string `json:"query_params"`
	PathParams      map[string]string   `json:"path_params"`
	ResponseHeaders map[string][]string `json:"response_headers"`
	Method          string              `json:"method"`
	SdkType         string              `json:"sdk_type"`
	Host            string              `json:"host"`
	RawURL          string              `json:"raw_url"`
	Referer         string              `json:"referer"`
	ProjectID       string              `json:"project_id"`
	URLPath         string              `json:"url_path"`
	ResponseBody    []byte              `json:"response_body"`
	RequestBody     []byte              `json:"request_body"`
	ProtoMinor      int                 `json:"proto_minor"`
	StatusCode      int                 `json:"status_code"`
	ProtoMajor      int                 `json:"proto_major"`
	Duration        time.Duration       `json:"duration"`
	Errors          []ATError           `json:"errors"`
	ServiceVersion  *string             `json:"service_version"`
	Tags            []string            `json:"tags"`
	MsgID           string              `json:"msg_id"`
	ParentID        *string             `json:"parent_id"`
}

Payload represents request and response details FIXME: How would we handle errors from background processes (Not web requests)

type RoundTripperOption

type RoundTripperOption func(*roundTripperConfig)

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) RoundTripperOption

WithHTTPClient allows you supply your own custom http client

func WithRedactHeaders

func WithRedactHeaders(headers ...string) RoundTripperOption

func WithRedactRequestBody

func WithRedactRequestBody(fields ...string) RoundTripperOption

func WithRedactResponseBody

func WithRedactResponseBody(fields ...string) RoundTripperOption

Jump to

Keyboard shortcuts

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