speakeasy

package module
v1.8.1 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2024 License: Apache-2.0 Imports: 36 Imported by: 1

README

speakeasy-go-sdk

180100416-b66263e6-1607-4465-b45d-0e298a67c397

Speakeasy is your API Platform team as a service. Use our drop in SDK to manage all your API Operations including embeds for request logs and usage dashboards, schema generation from traffic, and understanding API drift.

The Speakeasy Go SDK for evaluating API requests/responses. Compatible with any API framework implemented on top of Go's native http library.

Requirements

Supported routers:

We also support custom HTTP frameworks:

  • gin-gonic/gin
  • labstack/echo

Usage

Speakeasy uses Go Modules to manage dependencies.

go get github.com/speakeasy-api/speakeasy-go-sdk
Minimum configuration

Sign up for free on our platform. After you've created a workspace and generated an API key enable Speakeasy in your API as follows:

Configure Speakeasy at the start of your main() function:

import "github.com/speakeasy-api/speakeasy-go-sdk"

func main() {
	// Configure the Global SDK
	speakeasy.Configure(speakeasy.Config {
		APIKey:		"YOUR API KEY HERE",	// retrieve from Speakeasy API dashboard.
		ApiID:		"YOUR API ID HERE", 	// enter a name that you'd like to associate captured requests with.
        // This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
        // No spaces allowed.
		VersionID:	"YOUR VERSION ID HERE",	// enter a version that you would like to associate captured requests with.
        // The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
        // e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
	})

    // Associate the SDK's middleware with your router
	r := mux.NewRouter()
	r.Use(speakeasy.Middleware)
}

Note: Additional middlewares are provided for different routers.

Build and deploy your app and that's it. Your API is being tracked in the Speakeasy workspace you just created and will be visible on the dashboard next time you log in. Visit our docs site to learn more.

Mux-based routers

For middlewares based on the net/http ServeMux interface, use speakeasy.MiddlewareWithMux.

Gin

For the gin framework, use speakeasy.GinMiddlware.

Advanced configuration

The Speakeasy SDK provides both a global and per Api configuration option. If you want to use the SDK to track multiple Apis or Versions from the same service you can configure individual instances of the SDK, like so:

import "github.com/speakeasy-api/speakeasy-go-sdk"

func main() {
	r := mux.NewRouter()

	// Configure a new instance of the SDK for the store API
	storeSDKInstance := speakeasy.New(speakeasy.Config {
		APIKey:		"YOUR API KEY HERE",	// retrieve from Speakeasy API dashboard.
		ApiID:		"store_api", 	   		// this is an ID you provide that you would like to associate captured requests with.
		VersionID:	"1.0.0",				// this is a Version you provide that you would like to associate captured requests with.
	})

	// Configure a new instance of the SDK for the product API
	productSDKInstance := speakeasy.New(speakeasy.Config {
		APIKey:		"YOUR API KEY HERE",	// retrieve from Speakeasy API dashboard.
		ApiID:		"product_api", 			// this is an ID you provide that you would like to associate captured requests with.
		VersionID:	"1.0.0",				// this is a Version you provide that you would like to associate captured requests with.
	})

    // The different instances of the SDK (with differnt IDs or even versions assigned) can be used to associate requests with different APIs and Versions.
	s := r.PathPrefix("/store").Subrouter()
	r.Use(storeSDKInstance.Middleware)

	s := r.PathPrefix("/products").Subrouter()
	r.Use(productSDKInstance.Middleware)
}

This allows multiple instances of the SDK to be associated with different routers or routes within your service.

On-Premise Configuration

The SDK provides a way to redirect the requests it captures to an on-premise deployment of the Speakeasy Platform. This is done through the use of environment variables listed below. These are to be set in the environment of your services that have integrated the SDK:

  • SPEAKEASY_SERVER_URL - The url of the on-premise Speakeasy Platform's GRPC Endpoint. By default this is grpc.prod.speakeasyapi.dev:443.
  • SPEAKEASY_SERVER_SECURE - Whether or not to use TLS for the on-premise Speakeasy Platform. By default this is true set to SPEAKEASY_SERVER_SECURE="false" if you are using an insecure connection.

Request Matching

The Speakeasy SDK out of the box will do its best to match requests to your provided OpenAPI Schema. It does this by extracting the path template used by one of the supported routers or frameworks above for each request captured and attempting to match it to the paths defined in the OpenAPI Schema, for example:

r := mux.NewRouter()
r.Use(sdkInstance.Middleware)
r.HandleFunc("/v1/users/{id}", MyHandler) // The path template "/v1/users/{id}" is captured automatically by the SDK

This isn't always successful or even possible, meaning requests received by Speakeasy will be marked as unmatched, and potentially not associated with your Api, Version or ApiEndpoints in the Speakeasy Dashboard.

To help the SDK in these situations you can provide path hints per request handler that match the paths in your OpenAPI Schema:

func MyHandler(w http.ResponseWriter, r *http.Request) {
	// Provide a path hint for the request using the OpenAPI Path Templating format: https://swagger.io/specification/#path-templating-matching
	ctrl, ok := speakeasy.MiddlewareController(req)
	if ok {
	    ctrl.PathHint("/v1/users/{id}")
    }
	// the rest of your handlers code
}

Notes:
Wildcard path matching in Echo & Chi will end up with a OpenAPI path paramater called {wildcard} which will only match single level values represented by the wildcard. This is a restriction of the OpenAPI spec (Detail Here). For example:

chi template: /user/{id}/path/* => openapi template: /user/{id}/path/{wildcard}

And in the above example a path like /user/1/path/some/sub/path won't match but /user/1/path/somesubpathstring will, as / characters are not matched in path paramters by the OpenAPI spec.

Capturing Customer IDs

To help associate requests with customers/users of your APIs you can provide a customer ID per request handler:

func MyHandler(w http.ResponseWriter, r *http.Request) {
	ctrl, ok := speakeasy.MiddlewareController(req)
    if ok {
        ctrl.CustomerID("a-customers-id") // This customer ID will be used to associate this instance of a request with your customers/users
    }
	// the rest of your handlers code
}

Note: This is not required, but is highly recommended. By setting a customer ID you can easily associate requests with your customers/users in the Speakeasy Dashboard, powering filters in the Request Viewer.

Masking sensitive data

Speakeasy can mask sensitive data in the query string parameters, headers, cookies and request/response bodies captured by the SDK. This is useful for maintaining sensitive data isolation, and retaining control over the data that is captured.

Using the Advanced Configuration section above you can completely ignore certain routes by not assigning the middleware to their router, causing the SDK to not capture any requests to that router.

But if you would like to be more selective you can mask certain sensitive data using our middleware controller allowing you to mask fields as needed in different handlers:

func MyHandler(w http.ResponseWriter, r *http.Request) {
	ctrl, _ := speakeasy.MiddlewareController(req)
	ctrl.Masking(speakeasy.WithRequestHeaderMask("Authorization")) // Mask the Authorization header in the request
	
	// the rest of your handlers code
}

The Masking function takes a number of different options to mask sensitive data in the request:

  • speakeasy.WithQueryStringMask - WithQueryStringMask will mask the specified query strings with an optional mask string.
  • speakeasy.WithRequestHeaderMask - WithRequestHeaderMask will mask the specified request headers with an optional mask string.
  • speakeasy.WithResponseHeaderMask - WithResponseHeaderMask will mask the specified response headers with an optional mask string.
  • speakeasy.WithRequestCookieMask - WithRequestCookieMask will mask the specified request cookies with an optional mask string.
  • speakeasy.WithResponseCookieMask - WithResponseCookieMask will mask the specified response cookies with an optional mask string.
  • speakeasy.WithRequestFieldMaskString - WithRequestFieldMaskString will mask the specified request body fields with an optional mask. Supports string fields only. Matches using regex.
  • speakeasy.WithRequestFieldMaskNumber - WithRequestFieldMaskNumber will mask the specified request body fields with an optional mask. Supports number fields only. Matches using regex.
  • speakeasy.WithResponseFieldMaskString - WithResponseFieldMaskString will mask the specified response body fields with an optional mask. Supports string fields only. Matches using regex.
  • speakeasy.WithResponseFieldMaskNumber - WithResponseFieldMaskNumber will mask the specified response body fields with an optional mask. Supports number fields only. Matches using regex.

Masking can also be done more globally on all routes or a selection of routes by taking advantage of middleware. Here is an example:

speakeasy.Configure(speakeasy.Config {
	APIKey:		"YOUR API KEY HERE",	// retrieve from Speakeasy API dashboard.
	ApiID:		"YOUR API ID HERE", 	// this is an ID you provide that you would like to associate captured requests with.
	VersionID:	"YOUR VERSION ID HERE",	// this is a Version you provide that you would like to associate captured requests with.
})

r := mux.NewRouter()
r.Use(speakeasy.Middleware)
r.Use(func (next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Mask the Authorization header in the request for all requests served by this middleware
		ctrl, _ := speakeasy.MiddlewareController(req)
		ctrl.Masking(speakeasy.WithRequestHeaderMask("Authorization"))
	})
})

Embedded Request Viewer Access Tokens

The Speakeasy SDK can generate access tokens for the Embedded Request Viewer that can be used to view requests captured by the SDK.

For documentation on how to configure filters, find that HERE.

Below are some examples on how to generate access tokens:

import "github.com/speakeasy-api/speakeasy-schemas/grpc/go/registry/embedaccesstoken"

ctx := context.Background()

// If the SDK is configured as a global instance, an access token can be generated using the `GenerateAccessToken` function on the speakeasy package.
accessToken, err := speakeasy.GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
	Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
		{
			Key:   "customer_id",
			Operator: "=",
			Value: "a-customer-id",
		},
	},
})

// If you have followed the `Advanced Configuration` section above you can also generate an access token using the `GenerateAccessToken` function on the sdk instance.
accessToken, err := storeSDKInstance.GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
	Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
		{
			Key:   "customer_id",
			Operator: "=",
			Value: "a-customer-id",
		},
	},
})

// Or finally if you have a handler that you would like to generate an access token from, you can get the SDK instance for that handler from the middleware controller and use the `GetEmbedAccessToken` function it.
func MyHandler(w http.ResponseWriter, r *http.Request) {
	ctrl, _ := speakeasy.MiddlewareController(req)
	accessToken, err := ctrl.GetSDKInstance().GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
		Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
			{
				Key:   "customer_id",
				Operator: "=",
				Value: "a-customer-id",
			},
		},
	})
	
	// the rest of your handlers code
}

Documentation

Index

Constants

View Source
const (
	DefaultStringMask = "__masked__"
	DefaultNumberMask = "-12321"
)

Variables

View Source
var (
	// ErrAPIKeyMissing is returned when the API Key is not provided at configuration time.
	ErrAPIKeyMissing = errors.New("API key is required")
	// ErrAPIIdMissing is returned when the Api ID is not provided at configuration time.
	ErrApiIDMissing = errors.New("ApiID is required")
	// ErrApiIDMalformed is returned when the Api ID is invalid.
	ErrApiIDMalformed = errors.New("ApiID is malformed")
	// ErrVersionIDMissing is returned when the Version ID is not provided at configuration time.
	ErrVersionIDMissing = errors.New("VersionID is required")
	// ErrVersionIDMalformed is returned when the Version ID is invalid.
	ErrVersionIDMalformed = errors.New("VersionID is malformed")
)
View Source
var GRPCIngestTimeout = 1 * time.Second

Functions

func Close added in v1.5.1

func Close() error

func Configure

func Configure(config Config)

Configure allows you to configure the default instance of the Speakeasy SDK. Use this if you will use the same API Key for all connected APIs.

func EchoMiddleware added in v0.0.3

func EchoMiddleware(next echo.HandlerFunc) echo.HandlerFunc

EchoMiddleware setups up the default SDK instance to start capturing requests from the echo http framework.

func GetEmbedAccessToken added in v1.3.0

func GetEmbedAccessToken(ctx context.Context, req *embedaccesstoken.EmbedAccessTokenRequest) (string, error)

func GinMiddleware added in v0.0.3

func GinMiddleware(c *gin.Context)

GinMiddleware setups up the default SDK instance to start capturing requests from the gin http framework.

func Middleware added in v0.0.1

func Middleware(next http.Handler) http.Handler

Middleware setups up the default SDK instance to start capturing requests from routers that support http.Handlers. Currently only gorilla/mux, go-chi/chi routers and the http.DefaultServerMux are supported for automatically capturing path hints. Otherwise path hints can be supplied by a handler through the speakeasy MiddlewareController.

func MiddlewareController added in v0.0.3

func MiddlewareController(r *http.Request) (*controller, bool)

MiddlewareController will return the speakeasy middleware controller from the current request, if the current request is monitored by the speakeasy middleware.

func MiddlewareWithMux added in v1.7.0

func MiddlewareWithMux(mux Mux, next http.Handler) http.Handler

MiddlewareWithMux setups up the default SDK instance to start capturing requests from routers based on the net/http ServeMux interface This should be used when not using the http.DefaultServeMux, such as when using a custom mux or something like DataDog's httptrace.NewServeMux().

func NewCaptureWriter added in v0.0.3

func NewCaptureWriter(origResW http.ResponseWriter, maxBuffer int) *captureWriter

Types

type Config

type Config struct {
	// APIKey is the API Key obtained from the Speakeasy platform for capturing requests to a particular workspace.
	APIKey string
	// ApiID is the ID of the Api to associate any requests captured by this instance of the SDK to.
	ApiID string
	// VersionID is the ID of the Api Version to associate any requests captured by this instance of the SDK to.
	VersionID       string
	OpenAPIDocument []byte
	GRPCDialer      func() func(context.Context, string) (net.Conn, error)
}

Config provides configuration for the Speakeasy SDK.

type DialerFunc added in v1.3.0

type DialerFunc func() func(context.Context, string) (net.Conn, error)

type GRPCClient added in v1.3.0

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

func (*GRPCClient) GetEmbedAccessToken added in v1.3.0

func (c *GRPCClient) GetEmbedAccessToken(ctx context.Context, req *embedaccesstoken.EmbedAccessTokenRequest) (string, error)

func (*GRPCClient) SendToIngest added in v1.3.0

func (c *GRPCClient) SendToIngest(ctx context.Context, req *ingest.IngestRequest)

type MaskingOption added in v1.2.0

type MaskingOption func(c *controller)

func WithQueryStringMask added in v1.2.0

func WithQueryStringMask(keys []string, masks ...string) MaskingOption

WithQueryStringMask will mask the specified query strings with an optional mask string. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all query strings. If the number of masks provided is equal to the number of query strings, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithRequestCookieMask added in v1.2.0

func WithRequestCookieMask(cookies []string, masks ...string) MaskingOption

WithRequestCookieMask will mask the specified request cookies with an optional mask string. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all cookies. If the number of masks provided is equal to the number of cookies, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithRequestFieldMaskNumber added in v1.2.0

func WithRequestFieldMaskNumber(fields []string, masks ...string) MaskingOption

WithRequestFieldMaskNumber will mask the specified request body fields with an optional mask. Supports number fields only. Matches using regex. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all fields. If the number of masks provided is equal to the number of fields, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "-12321").

func WithRequestFieldMaskString added in v1.2.0

func WithRequestFieldMaskString(fields []string, masks ...string) MaskingOption

WithRequestFieldMaskString will mask the specified request body fields with an optional mask. Supports string fields only. Matches using regex. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all fields. If the number of masks provided is equal to the number of fields, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithRequestHeaderMask added in v1.2.0

func WithRequestHeaderMask(headers []string, masks ...string) MaskingOption

WithRequestHeaderMask will mask the specified request headers with an optional mask string. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all headers. If the number of masks provided is equal to the number of headers, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithResponseCookieMask added in v1.2.0

func WithResponseCookieMask(cookies []string, masks ...string) MaskingOption

WithResponseCookieMask will mask the specified response cookies with an optional mask string. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all cookies. If the number of masks provided is equal to the number of cookies, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithResponseFieldMaskNumber added in v1.2.0

func WithResponseFieldMaskNumber(fields []string, masks ...string) MaskingOption

WithResponseFieldMaskNumber will mask the specified response body with an optional mask. Supports number fields only. Matches using regex. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all fields. If the number of masks provided is equal to the number of fields, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "-12321").

func WithResponseFieldMaskString added in v1.2.0

func WithResponseFieldMaskString(fields []string, masks ...string) MaskingOption

WithResponseFieldMaskString will mask the specified response body with an optional mask. Supports string only. Matches using regex. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all fields. If the number of masks provided is equal to the number of fields, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

func WithResponseHeaderMask added in v1.2.0

func WithResponseHeaderMask(headers []string, masks ...string) MaskingOption

WithResponseHeaderMask will mask the specified response headers with an optional mask string. If no mask is provided, the value will be masked with the default mask. If a single mask is provided, it will be used for all headers. If the number of masks provided is equal to the number of headers, masks will be used in order. Otherwise, the masks will be used in order until it they are exhausted. If the masks are exhausted, the default mask will be used. (defaults to "__masked__").

type Mux added in v1.7.0

type Mux interface {
	Handler(r *http.Request) (h http.Handler, pattern string)
}

Mux represents a router that conforms to the net/http ServeMux interface.

type Speakeasy added in v1.1.0

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

Speakeasy is the concrete type for the Speakeasy SDK. Don't instantiate this directly, use Configure() or New() instead.

func New added in v0.0.1

func New(config Config) *Speakeasy

New creates a new instance of the Speakeasy SDK. This allows you to create multiple instances of the SDK for specifying different API Keys for different APIs.

func (*Speakeasy) Close added in v1.5.1

func (s *Speakeasy) Close() error

func (*Speakeasy) EchoMiddleware added in v1.1.0

func (s *Speakeasy) EchoMiddleware(next echo.HandlerFunc) echo.HandlerFunc

EchoMiddleware setups the current instance of the SDK to start capturing requests from the echo http framework.

func (*Speakeasy) GetEmbedAccessToken added in v1.3.0

func (s *Speakeasy) GetEmbedAccessToken(ctx context.Context, req *embedaccesstoken.EmbedAccessTokenRequest) (string, error)

func (*Speakeasy) GinMiddleware added in v1.1.0

func (s *Speakeasy) GinMiddleware(c *gin.Context)

GinMiddleware setups the current instance of the SDK to start capturing requests from the gin http framework.

func (*Speakeasy) MatchOpenAPIPath added in v1.8.0

func (s *Speakeasy) MatchOpenAPIPath(r *http.Request) string

func (*Speakeasy) Middleware added in v1.1.0

func (s *Speakeasy) Middleware(next http.Handler) http.Handler

Middleware setups the current instance of the SDK to start capturing requests from routers that support http.Handlers. Currently only gorilla/mux, go-chi/chi routers and the http.DefaultServerMux are supported for automatically capturing path hints. Otherwise path hints can be supplied by a handler through the speakeasy MiddlewareController.

func (*Speakeasy) MiddlewareWithMux added in v1.7.0

func (s *Speakeasy) MiddlewareWithMux(mux Mux, next http.Handler) http.Handler

MiddlewareWithMux setups up the current instance of the SDK to start capturing requests from routers based on the net/http ServeMux interface This should be used when not using the http.DefaultServeMux, such as when using a custom mux or something like DataDog's httptrace.NewServeMux().

Directories

Path Synopsis
internal
log

Jump to

Keyboard shortcuts

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