Golang type-safe dependency injection

nject, npoint, nserve, & nvelope - dependency injection

GoDoc unit tests report card codecov FOSSA Status

Install:

go get github.com/muir/nject

This is a quartet of packages that together make up a most of a golang API server framework:

nject: type safe dependency injection w/o requiring type assertions.

npoint: dependency injection wrappers for binding http endpoint handlers

nvelope: injection chains for building endpoints

nserve: injection chains for for starting and stopping servers

Basic idea

Dependencies are injected via a call chain: list functions to be called that take and return various parameters. The functions will be called in order using the return values from earlier functions as parameters for later functions.

Parameters are identified by their types. To have two different int parameters, define custom types.

Type safety is checked before any functions are called.

Functions whose outputs are not used are not called. Functions may "wrap" the rest of the list so that they can choose to invoke the remaing list zero or more times.

Chains may be pre-compiled into closures so that they have very little runtime penealty.

nject example

func example() {
	// Sequences can be reused.
	providerChain := Sequence("example sequence",
		// Constants can be injected.
		"a literal string value",
		// This function will be run if something downstream needs an int
		func(s string) int {
			return len(s)
		})
	Run("example",
		providerChain,
		// The last function in the list is always run.  This one needs
		// and int and a string.  The string can come from the constant
		// and the int from the function in the provider chain.
		func(i int, s string) {
			fmt.Println(i, len(s))
		})
}

npoint example

CreateEndpoint is the simplest way to start using the npoint framework. It generates an http.HandlerFunc from a list of handlers. The handlers will be called in order. In the example below, first WriteErrorResponse() will be called. It has an inner() func that it uses to invoke the rest of the chain. When WriteErrorResponse() calls its inner() function, the db injector returned by InjectDB is called. If that does not return error, then the inline function below to handle the endpint is called.

mux := http.NewServeMux()
mux.HandleFunc("/my/endpoint", npoint.CreateEndpoint(
	WriteErrorResponse,
	InjectDB("postgres", "postgres://..."),
	func(r *http.Request, db *sql.DB, w http.ResponseWriter) error {
		// Write response to w or return error...
		return nil
	}))

WriteErrorResponse invokes the remainder of the handler chain by calling inner().

func WriteErrorResponse(inner func() nject.TerminalError, w http.ResponseWriter) {
	err := inner()
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
	}
}

InjectDB returns a handler function that opens a database connection. If the open fails, executation of the handler chain is terminated. InjectDB returns an injector so that it can be called with arguments -- injectors are functions, not invocations and so we need to return a function. InjectDB also closes the database connection.

func InjectDB(driver, uri string) func(func(*sql.DB) error) error {
	return func(inner func(*sql.DB) error) (finalError error) {
		db, err := sql.Open(driver, uri)
		if err != nil {
			return err
		}
		defer func() {
			err := db.Close()
			if err != nil && finalError == nil {
				finalError = err
			}
		}()
		return inner(db)
	}
}

nvelope example

Nvelope provides pre-defined handlers for basic endpoint tasks. When used in combination with npoint, all that's left is the business logic.

type ExampleRequestBundle struct {
	Request     PostBodyModel `nvelope:"model"`
	With        string        `nvelope:"path,name=with"`
	Parameters  int64         `nvelope:"path,name=parameters"`
	Friends     []int         `nvelope:"query,name=friends"`
	ContentType string        `nvelope:"header,name=Content-Type"`
}

func Service(router *mux.Router) {
	service := npoint.RegisterServiceWithMux("example", router)
	service.RegisterEndpoint("/some/path",
		nvelope.LoggerFromStd(log.Default()),
		nvelope.InjectWriter,
		nvelope.EncodeJSON,
		nvelope.CatchPanic,
		nvelope.Nil204,
		nvelope.ReadBody,
		nvelope.DecodeJSON,
		func (req ExampleRequestBundle) (nvelope.Response, error) {
			....
		},
	).Methods("POST")
}

nserve example

On thing you might want to do with nserve is to use a Hook to trigger per-library database migrations using libschema.

First create the hook:

package myhooks

import "github.com/nject/nserve"

var MigrateMyDB = nserve.NewHook("migrate, nserve.Ascending)

In each library, have a create function:

package users

import(
	"github.com/muir/libschema/lspostgres"
	"github.com/muir/nject/nserve"
)

func NewUsersStore(app *nserve.App) *Store {
	...
	app.On(myhooks.MigrateMyDB, func(database *libschema.Database) {
		database.Migrations("MyLibrary",
			lspostgres.Script("create users", `
				CREATE TABLE users (
					id	bigint PRIMARY KEY,
					name	text
				)
			`),
		)
	})
	...
	return &Store{}
}

Then as part of server startup, invoke the migration hook:

package main

import(
	"github.com/muir/libschema"
	"github.com/muir/libschema/lspostgres"
	"github.com/muir/nject/nject"
)

func main() {
	app, err := nserve.CreateApp("myApp", users.NewUserStore, ...)
	schema := libschema.NewSchema(ctx, libschema.Options{})
	sqlDB, err := sql.Open("postgres", "....")
	database, err := lspostgres.New(logger, "main-db", schema, sqlDB)
	myhooks.MigrateMyDB.Using(database)
	err = app.Do(myhooks.MigrateMyDB)

Development status

This repo represents continued development of Blue Owl's nject base. Blue Owl's code has been in production use for years and has been unchanged for years. The core of nject is mostly unchanged. Nvelope and nserve are new.

Go version

Due to the use of strconv.ParseComplex in nvelope, the minimum supported version of Go is 1.15

Owner
Comments
  • build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1

    build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1

    Bumps github.com/stretchr/testify from 1.7.0 to 1.7.1.

    Commits
    • 083ff1c Fixed didPanic to now detect panic(nil).
    • 1e36bfe Use cross Go version compatible build tag syntax
    • e798dc2 Add docs on 1.17 build tags
    • 83198c2 assert: guard CanConvert call in backward compatible wrapper
    • 087b655 assert: allow comparing time.Time
    • 7bcf74e fix msgAndArgs forwarding
    • c29de71 add tests for correct msgAndArgs forwarding
    • f87e2b2 Update builds
    • ab6dc32 fix linting errors in /assert package
    • edff5a0 fix funtion name
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • build(deps): bump github.com/muir/reflectutils from 0.0.1 to 0.0.2

    build(deps): bump github.com/muir/reflectutils from 0.0.1 to 0.0.2

    Bumps github.com/muir/reflectutils from 0.0.1 to 0.0.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • bugfix: Reorder was duplicating injectors

    bugfix: Reorder was duplicating injectors

    • bugfix: Reorder was duplicating injectors
    • reorder now bombs out if the count of injectors doesn't match
    • added a regression test
    • small improvement to regression test generation
  • Bug fixes for regression test generation and OverridesError

    Bug fixes for regression test generation and OverridesError

    • DetailedError() did not have a test [fixed]
    • OverridesError() only partially worked [fixed]
    • OverridesError() test was incorrect [fixed]
    • Automatic creation of regression tests missed many decorators [fixed]
    • README now has instructions for getting generated regression tests
  • build(deps): bump github.com/stretchr/testify from 1.7.4 to 1.7.5

    build(deps): bump github.com/stretchr/testify from 1.7.4 to 1.7.5

    Bumps github.com/stretchr/testify from 1.7.4 to 1.7.5.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • build(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2

    build(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2

    Bumps github.com/stretchr/testify from 1.7.1 to 1.7.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Add Condense() and SaveTo()

    Add Condense() and SaveTo()

    SaveTo() added. Intermediate values can now be requested and written through pointers.

    Collection.Condense() added -- a collection can now be turned into a single injector. This can be done recursively. Combined with SaveTo, multiple copies of the same type can be generated and preserved separately.

    The Reflective type has been joined by new siblings: ReflectiveInvoker, ReflectiveArgs, and ReflectiveWrapper

  • Add a Reorder() decorator

    Add a Reorder() decorator

    Reorder allows a provider move to a different part of the injection chain. This is useful when part of an injection chain is prebuilt and part is comes from another source and it's useful to pull some of the extra providers can provide inputs to the prebuilt portion.

    Also a bugfix: Shun() wasn't given the priority it needed. It now is.

  • build(deps): bump github.com/muir/reflectutils from 0.3.1 to 0.4.0

    build(deps): bump github.com/muir/reflectutils from 0.3.1 to 0.4.0

    Bumps github.com/muir/reflectutils from 0.3.1 to 0.4.0.

    Release notes

    Sourced from github.com/muir/reflectutils's releases.

    time.Duration & flag.Value

    Add support for registering unpackers and use that to support time.Duration. Add support for unpacking into things that support the flag.Value interface

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • build(deps): bump github.com/muir/reflectutils from 0.2.2 to 0.3.0

    build(deps): bump github.com/muir/reflectutils from 0.2.2 to 0.3.0

    Bumps github.com/muir/reflectutils from 0.2.2 to 0.3.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • build(deps): bump github.com/muir/reflectutils from 0.2.1 to 0.2.2

    build(deps): bump github.com/muir/reflectutils from 0.2.1 to 0.2.2

    Bumps github.com/muir/reflectutils from 0.2.1 to 0.2.2.

    Release notes

    Sourced from github.com/muir/reflectutils's releases.

    expose TagSet.Tag

    also: split on "" means don't split

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
alog is a dependency free, zero/minimum memory allocation JSON logger with extensions
alog is a dependency free, zero/minimum memory allocation JSON logger with extensions

Alog (c) 2020-2021 Gon Y Yi. https://gonyyi.com. MIT License Version 1.0.0 Intro Alog was built with a very simple goal in mind: Support Tagging (and

Dec 13, 2021
Simple and blazing fast lockfree logging library for golang
Simple and blazing fast lockfree logging library for golang

glg is simple golang logging library Requirement Go 1.11 Installation go get github.com/kpango/glg Example package main import ( "net/http" "time"

Nov 28, 2022
Logging library for Golang

GLO Logging library for Golang Inspired by Monolog for PHP, severity levels are identical Install go get github.com/lajosbencz/glo Severity levels Deb

Sep 26, 2022
a golang log lib supports level and multi handlers

go-log a golang log lib supports level and multi handlers Use import "github.com/siddontang/go-log/log" //log with different level log.Info("hello wo

Dec 29, 2022
LogVoyage - logging SaaS written in GoLang
LogVoyage - logging SaaS written in GoLang

No longer maintained, sorry. Completely rewritten v2 is going to be released soon. Please follow http://github.com/logvoyage LogVoyage - fast and simp

Sep 26, 2022
An golang log lib, supports tracking and level, wrap by standard log lib

Logex An golang log lib, supports tracing and level, wrap by standard log lib How To Get shell go get gopkg.in/logex.v1 source code import "gopkg.in/

Nov 27, 2022
Utilities for slightly better logging in Go (Golang).

logutils logutils is a Go package that augments the standard library "log" package to make logging a bit more modern, without fragmenting the Go ecosy

Dec 16, 2022
Dead simple, super fast, zero allocation and modular logger for Golang

Onelog Onelog is a dead simple but very efficient JSON logger. It is one of the fastest JSON logger out there. Also, it is one of the logger with the

Sep 26, 2022
A Go (golang) package providing high-performance asynchronous logging, message filtering by severity and category, and multiple message targets.

ozzo-log Other languages 简体中文 Русский Description ozzo-log is a Go package providing enhanced logging support for Go programs. It has the following fe

Dec 17, 2022
Golang logging library
Golang logging library

Golang logging library Package logging implements a logging infrastructure for Go. Its output format is customizable and supports different logging ba

Dec 27, 2022
A feature-rich and easy to use logger for golang
A feature-rich and easy to use logger for golang

A feature-rich and easy to use logger for golang ?? Install ?? Common Logs lumber.Success() lumber.Info() lumber.Debug() lumber.Warning()

Dec 31, 2022
A system and resource monitoring tool written in Golang!
A system and resource monitoring tool written in Golang!

Grofer A clean and modern system and resource monitor written purely in golang using termui and gopsutil! Currently compatible with Linux only. Curren

Jan 8, 2023
Golang beautify data display for Humans

Golang beautify data display for Humans English 简体中文 Usage Examples package main import ( ffmt "gopkg.in/ffmt.v1" ) func main() { example() } typ

Dec 22, 2022
Logstash like, written in golang

gogstash Logstash like, written in golang Download gogstash from github check latest version Use docker image tsaikd/gogstash curl 'https://github.com

Dec 18, 2022
Open Source Supreme Monitor Based on GoLang

Open Source Supreme Monitor Based on GoLang A module built for personal use but ended up being worthy to have it open sourced.

Nov 4, 2022
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.

Noodlog Summary Noodlog is a Golang JSON parametrized and highly configurable logging library. It allows you to: print go structs as JSON messages; pr

Oct 27, 2022
HTTP request logger for Golang
HTTP request logger for Golang

Horus ?? Introduction Horus is a request logger and viewer for Go. It allows developers log and view http requests made to their web application. Inst

Dec 27, 2022
Example instrumentation of Golang Application with OpenTelemetry with supported configurations to export to Sentry.
Example instrumentation of Golang Application with OpenTelemetry with supported configurations to export to Sentry.

Sentry + Opentelemetry Go Example Requirements To run this example, you will need a kubernetes cluster. This example has been tried and tested on Mini

Oct 27, 2022