cassette

package
v0.0.0-...-05a982e Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsMasked

func IsMasked(s string) bool

IsMasked reports whether s is a placeholder produced by masking.

Such a value carries no information a reader does not already have: the inferred format says "uuid" far more usefully than an example of "00000000-0000-0000-0000-000000000000" does. Callers building documentation from a masked recording can use this to leave the example out entirely.

func RedactsBodyKey

func RedactsBodyKey(key string) bool

RedactsBodyKey reports whether DefaultMasker redacts values held by key.

A masked string announces itself — the zero UUID and the asterisk run are recognisable by IsMasked — but a masked number does not: a redacted count becomes 0, which is indistinguishable from a genuine 0. The key is the only remaining signal, so callers documenting a masked recording can ask about it directly rather than guessing from the value.

Types

type Body

type Body []byte

func (Body) MarshalJSONTo

func (b Body) MarshalJSONTo(enc *jsontext.Encoder) error

func (*Body) UnmarshalJSONFrom

func (b *Body) UnmarshalJSONFrom(dec *jsontext.Decoder) error

type Interaction

type Interaction struct {
	Request  Request  `json:"request"`
	Response Response `json:"response,omitzero"`
}

Interaction represents a single observed HTTP request/response pair.

type Interactions

type Interactions []Interaction

Interactions represents a collection of interactions.

func InteractionsReadFile

func InteractionsReadFile(path string) (Interactions, error)

func InteractionsUnmarshal

func InteractionsUnmarshal(data []byte) (Interactions, error)

func InteractionsUnmarshalRead

func InteractionsUnmarshalRead(r io.Reader) (Interactions, error)

func (Interactions) MarshalWrite

func (ias Interactions) MarshalWrite(w io.Writer) error

func (Interactions) Mask

func (ias Interactions) Mask()

Mask redacts sensitive values in place using DefaultMasker.

func (Interactions) MaskWith

func (ias Interactions) MaskWith(m Masker)

MaskWith redacts sensitive values in place according to m.

func (Interactions) TrimResponseHeaders

func (ias Interactions) TrimResponseHeaders()

TrimResponseHeaders removes response headers we don't need in place.

func (Interactions) WriteFile

func (ias Interactions) WriteFile(path string) error

type Masker

type Masker struct {
	// HeaderKeys are header names whose values are redacted, in requests and
	// responses alike. Matching is case-insensitive.
	HeaderKeys []string

	// BodyKeys are JSON object keys whose values are redacted wherever they
	// occur in a request or response body. Matching is case-insensitive and
	// exact: "id" does not match "ownerId". When a matching key holds an object
	// or an array, the whole subtree below it is redacted.
	BodyKeys []string

	// IDKeys are keys holding identifiers, redacted only when the value looks
	// machine-generated: a UUID, or a run of at least 16 characters that has no
	// separators and contains a digit.
	//
	// Use this for keys far too common to redact outright. "id" holding an
	// account UUID or a numeric account ID is caught, while "id" holding a
	// structural name — Notion labels its title property "title" — is left alone.
	IDKeys []string

	// NameKeys are keys holding a person's name, replaced with "John Doe" when
	// the value actually reads as one: two to four capitalised words of letters.
	//
	// The shape check matters as much as the key. Recorded data is full of
	// capitalised phrases that are not people — company names, taxonomy labels,
	// page titles — and replacing those would corrupt the fixture while
	// protecting nobody.
	NameKeys []string

	// UsernameKeys are keys holding a handle, replaced with "johndoe" in the
	// same case as the original: "friedamuster" becomes "johndoe",
	// "FriedaMuster" becomes "JohnDoe", "@friedamuster" becomes "@johndoe".
	//
	// Casing is preserved because an API often stores the same handle several
	// ways — Habitica keeps both username and lowerCaseUsername — and a
	// replacement that flattened them would make the fixture describe something
	// the API never returns.
	UsernameKeys []string

	// Values are literal strings redacted wherever they occur in a body,
	// whatever key holds them. Use this for identifiers that live under keys too
	// generic to redact wholesale — an account UUID appearing under "id",
	// "ownerId" and "value" is caught by listing the UUID here, whereas listing
	// those keys in BodyKeys would destroy unrelated data.
	//
	// Prefer the key-based fields where they suffice: a value listed here has to
	// be written down somewhere, and a masker configured in tracked source would
	// publish the very string it is meant to hide.
	Values []string

	// KeepEmails leaves e-mail addresses in place. By default any string that is
	// an e-mail address is redacted wherever it occurs, whatever key holds it,
	// because an address is personal data no matter where it was recorded and
	// the key holding it is often too generic to list.
	KeepEmails bool
}

Masker configures which recorded values Interactions.MaskWith redacts.

Redaction is shape-preserving: a masked e-mail address is replaced by another e-mail address, a masked UUID by another UUID, a masked number by a number. Schemas inferred from a masked recording therefore keep the types and formats they would have had from the original, so masking a cassette does not change the specification it produces — only the examples in it.

The placeholders describe one fictional person: John Doe, johndoe, john.doe@example.com. A reader who meets one of them recognises the rest.

func DefaultMasker

func DefaultMasker() Masker

DefaultMasker returns the configuration used by Interactions.Mask: the headers and body keys that carry credentials in most APIs.

It knows nothing about which identifiers are yours. A recording that carries an account ID under a generic key such as "id" needs that key listed in Masker.IDKeys, or the value itself in Masker.Values.

type Request

type Request struct {
	Method  string      `json:"method"`
	URL     string      `json:"url"`
	Headers http.Header `json:"header,omitempty"`
	Body    Body        `json:"body,omitempty"`
}

Request represents an observed HTTP request.

func NewRequest

func NewRequest(req *http.Request) (Request, error)

NewRequest creates a new Request out of an *http.Request. If the request has a body, it is drained and restored.

func (Request) Create

func (r Request) Create(ctx context.Context) (*http.Request, error)

Create creates a corresponding *http.Request.

type Response

type Response struct {
	StatusCode int         `json:"statusCode"`
	Headers    http.Header `json:"header,omitempty"`
	Body       Body        `json:"body,omitempty"`
}

Response represents an observed HTTP response.

func NewResponse

func NewResponse(resp *http.Response) (Response, error)

NewResponse creates a new Response out of an *http.Response. If the response has a body, it is drained and restored.

Jump to

Keyboard shortcuts

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