fxjsonapi

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2025 License: MIT Imports: 18 Imported by: 0

README

Yokai JSON API Module

ci go report codecov Deps PkgGoDev

Yokai module for JSON API, based on google/jsonapi.

Overview

This module provides to your Yokai application a Processor, that you can inject in your HTTP handlers to process JSON API requests and responses.

It also provides automatic error handling, compliant with the JSON API specifications.

Installation

Install the module:

go get github.com/ankorstore/yokai-contrib/fxjsonapi

Then activate it in your application bootstrapper:

// internal/bootstrap.go
package internal

import (
	"github.com/ankorstore/yokai-contrib/fxjsonapi"
	"github.com/ankorstore/yokai/fxcore"
)

var Bootstrapper = fxcore.NewBootstrapper().WithOptions(
	// load modules
	fxjsonapi.FxJSONAPIModule,
	// ...
)

Configuration

Configuration reference:

# ./configs/config.yaml
modules:
  jsonapi:
    log:
      enabled: true # to automatically log JSON API processing, disabled by default
    trace:
      enabled: true # to automatically trace JSON API processing, disabled by default

Processing

Request processing

You can use the provided Processor to automatically process a JSON API request:

package handler

import (
	"net/http"

	"github.com/ankorstore/yokai-contrib/fxjsonapi"
	"github.com/google/jsonapi"
	"github.com/labstack/echo/v4"
)

type Foo struct {
	ID   int    `jsonapi:"primary,foo"`
	Name string `jsonapi:"attr,name"`
	Bar  *Bar   `jsonapi:"relation,bar"`
}

func (f Foo) JSONAPIMeta() *jsonapi.Meta {
	return &jsonapi.Meta{
		"some": "foo meta",
	}
}

type Bar struct {
	ID   int    `jsonapi:"primary,bar"`
	Name string `jsonapi:"attr,name"`
}

func (b Bar) JSONAPIMeta() *jsonapi.Meta {
	return &jsonapi.Meta{
		"some": "bar meta",
	}
}

type JSONAPIHandler struct {
	processor fxjsonapi.Processor
}

func NewJSONAPIHandler(processor fxjsonapi.Processor) *JSONAPIHandler {
	return &JSONAPIHandler{
		processor: processor,
	}
}

func (h *JSONAPIHandler) Handle() echo.HandlerFunc {
	return func(c echo.Context) error {
		foo := Foo{}

		// unmarshall JSON API request payload in foo
		err := h.processor.ProcessRequest(
			// echo context
			c,
			// pointer to the struct to unmarshall
			&foo,
			// optionally override module config for logging
			fxjsonapi.WithLog(true),
			// optionally override module config for tracing
			fxjsonapi.WithTrace(true),
			)
		if err != nil {
			return err
		}

		return c.JSON(http.StatusOK, foo)
	}
}

Notes about ProcessRequest():

  • if the request payload does not respect the JSON API specifications, a 400 error will be automatically returned
  • if the request Content-Type header is not application/vnd.api+json, a 415 error will be automatically returned
Response processing

You can use the provided Processor to automatically process a JSON API response:

package handler

import (
	"net/http"

	"github.com/ankorstore/yokai-contrib/fxjsonapi"
	"github.com/ankorstore/yokai-contrib/fxjsonapi/testdata/model"
	"github.com/labstack/echo/v4"
)

type Foo struct {
	ID   int    `jsonapi:"primary,foo"`
	Name string `jsonapi:"attr,name"`
	Bar  *Bar   `jsonapi:"relation,bar"`
}

func (f Foo) JSONAPIMeta() *jsonapi.Meta {
	return &jsonapi.Meta{
		"some": "foo meta",
	}
}

type Bar struct {
	ID   int    `jsonapi:"primary,bar"`
	Name string `jsonapi:"attr,name"`
}

func (b Bar) JSONAPIMeta() *jsonapi.Meta {
	return &jsonapi.Meta{
		"some": "bar meta",
	}
}

type JSONAPIHandler struct {
	processor fxjsonapi.Processor
}

func NewJSONAPIHandler(processor fxjsonapi.Processor) *JSONAPIHandler {
	return &JSONAPIHandler{
		processor: processor,
	}
}

func (h *JSONAPIHandler) Handle() echo.HandlerFunc {
	return func(c echo.Context) error {
		foo := Foo{
			ID:   123,
			Name: "foo",
			Bar: &Bar{
				ID:   456,
				Name: "bar",
			},
		}

		return h.processor.ProcessResponse(
			// echo context
			c,
			// HTTP status code
			http.StatusOK,
			// pointer to the struct to marshall
			&foo,
			// optionally pass metadata to the JSON API response
			fxjsonapi.WithMetadata(map[string]interface{}{
				"some": "response meta",
			}),
			// optionally remove the included from the JSON API response (enabled by default)
			fxjsonapi.WithIncluded(false),
			// optionally override module config for logging
			fxjsonapi.WithLog(true),
			// optionally override module config for tracing
			fxjsonapi.WithTrace(true),
		)
	}
}

Notes about ProcessResponse():

  • you can pass a pointer or a slice of pointers to marshall as JSON API
  • application/vnd.api+json will be automatically added to the response Content-Type header

Error handling

This module automatically enables the ErrorHandler, to convert errors bubbling up in JSON API format.

It handles:

  • JSON API errors: automatically sets the status code of the error
  • validation errors: automatically sets a 400 status code
  • HTTP errors: automatically sets the status code of the error
  • or any generic error: automatically sets a 500 status code

You can optionally obfuscate the errors details (ex: for production) in the HTTP server configuration:

# ./configs/config.yaml
modules:
  http:
    server:
      errors:
        obfuscate: true # disabled by default

Testing

This module provides a ProcessorMock for mocking Processor, see usage example.

Documentation

Index

Constants

View Source
const ModuleName = "jsonapi"

ModuleName is the module name.

Variables

FxJSONAPIModule is the Fx JSON API module.

Functions

func Marshall

func Marshall(data any, params MarshallParams) ([]byte, error)

Marshall is used to marshall in json api format a given input with MarshallParams.

Types

type DefaultProcessor

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

DefaultProcessor is the default Processor implementation.

func NewDefaultProcessor

func NewDefaultProcessor(config *config.Config) *DefaultProcessor

NewDefaultProcessor returns a new DefaultProcessor instance.

func ProvideProcessor

func ProvideProcessor(p ProvideProcessorParam) *DefaultProcessor

ProvideProcessor provides a new DefaultProcessor instance.

func (*DefaultProcessor) ProcessRequest

func (p *DefaultProcessor) ProcessRequest(c echo.Context, data any, options ...ProcessorOption) error

ProcessRequest processes a json api request.

func (*DefaultProcessor) ProcessResponse

func (p *DefaultProcessor) ProcessResponse(c echo.Context, code int, data any, options ...ProcessorOption) error

ProcessResponse processes a json api response.

type ErrorHandler

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

ErrorHandler is an Echo error handler for JSON APIs.

func NewErrorHandler

func NewErrorHandler(config *config.Config) *ErrorHandler

NewErrorHandler provides a new ErrorHandler instance.

func (*ErrorHandler) Handle

func (h *ErrorHandler) Handle() echo.HTTPErrorHandler

Handle returns the Echo compatible echo.HTTPErrorHandler.

type MarshallParams

type MarshallParams struct {
	Metadata        map[string]interface{}
	WithoutIncluded bool
}

type Options

type Options struct {
	Metadata map[string]any
	Included bool
	Log      bool
	Trace    bool
}

Options are options for the Processor.

func DefaultProcessorOptions

func DefaultProcessorOptions(config *config.Config) Options

DefaultProcessorOptions are the default Processor options.

type Processor

type Processor interface {
	ProcessRequest(c echo.Context, data any, options ...ProcessorOption) error
	ProcessResponse(c echo.Context, code int, data any, options ...ProcessorOption) error
}

Processor is the interface for json api processors implementations.

type ProcessorOption

type ProcessorOption func(o *Options)

ProcessorOption are functional options for the Processor.

func WithIncluded

func WithIncluded(i bool) ProcessorOption

WithIncluded is used to add included to the json api representation.

func WithLog

func WithLog(l bool) ProcessorOption

WithLog is used to add logging.

func WithMetadata

func WithMetadata(m map[string]any) ProcessorOption

WithMetadata is used to add metadata to the json api representation.

func WithTrace

func WithTrace(t bool) ProcessorOption

WithTrace is used to add tracing.

type ProvideProcessorParam

type ProvideProcessorParam struct {
	fx.In
	Config *config.Config
}

ProvideProcessorParam allows injection of the required dependencies in ProvideProcessor.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳