forms

package module
v0.0.0-...-939da3a Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2021 License: Apache-2.0 Imports: 21 Imported by: 1

README

coopgo/forms

made-with-Go GitHub go.mod Go version of a Go module go.dev reference

coopgo/forms is a web service written in Golang to handle forms data using différent data formats, protocols and backends

Communication protocols (transports) :

  • HTTP API (REST)
  • gRPC

Multiple transports can be run at the same time.

Different data backends / storages can be set :

  • Memory (temporary storage, for development/testing purpose)
  • PostgreSQL database (might also work with CockroachDB but not tested yet as we use the pgx driver compatible with both PostgreSQL and CockroachDB). PostgreSQL backend relies heavily on database tables/schemas generated on the fly (each table stores data from one form, even for unstructured forms, without defined schemas), so that we can easily plug another tool to visualize data like Apache Superset, Metabase or NocoDB.
  • Kantree (one way) (TODO : add some documentation about this)
  • Email with SMTP (one way)
  • Webhooks (one way)

"One way" backends do not store the form states. They could be better considered as "connectors" to other services, but are designed as the other backends in the code (they implement the same interface Backend).

At least one storage has to be set at the service level globally. These global storages cannot be "one way". Supported "global" storages are at the moment : Memory (temporary storage : only for development) and PostgreSQL

Multiple additional storages can be set for each form individually.

Our priority is to support memory (for easy development purpose), PostgreSQL, Kantree and Email (because that's our internal needs at COOPGO), but feel free to contribute something else (other databases, connectors to other services like chat apps, webhooks, etc...) if you want it first.

We have different types of forms :

  • Structured forms : forms with a defined schema. Every response must match the given schema. Structured forms schemas are defined using the JSON Type Definition standard.
  • Unstructured forms : forms accepting any input

Project Status

This project is still in development. It should be kind of functional using the PostgreSQL backend for simple use cases. Efforts still need to be made to support more complex forms with PostgreSQL and to implement other backends.

gRPC implementation and development considerations

This project uses protoc and protoc-gen-go-grpc to handle gRPC transport. You need to install and run them to build the project of changing anything on the gRPC side.

See https://grpc.io/docs/languages/go/quickstart/ for more details on how it works.

If you already know basics on this and have everything installed correctly, here is the command line to generate GRPC/Protocol buffers code inside the grpc package :

$ protoc -I grpc/ grpc/*.proto --go-grpc_out=./ --go_out=./

Contributing

We welcome any contributions following theses guidelines :

  • Any change to the code of the library must not break any transport or backend. Any new feature should be implemented for all if applicable.
  • Write simple, clear and maintainable code and avoid technical debt.
  • Leave the code cleaner than when you started.
  • Refactoring existing code for better performance, better readability or better testing wins over creating a new feature.

If you want to contribute, you can fork the repository and create a pull request.

Bug report

For reporting a bug, you can open an issue using the Bug Report template. Try to write a bug report that is easy to understand and explain how to reproduce the bug. Do not duplicate an existing issue and keep each issue specific to an individual bug.

License

coopgo/forms is under the Apache 2.0 license. Please refer to the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Backend

type Backend interface {
	Type() string
	Configuration() map[string]interface{}
	AddForm(Form) error
	DeleteForm(string) error
	GetForms() ([]Form, error)
	GetForm(string) (Form, error)
	SubmitResponse(Form, Response) error
	GetFormResponses(string) ([]Response, error)
	AddFormBackend(string, Backend) error
	GetFormBackends(string) ([]Backend, error)
}

Backend is the interface for backends (storage, data handling)

type DataType

type DataType int

DataType is the type to describe the forms data types (Text, Email, Boolean, Number, Location, ...)

const (
	TextType DataType = iota
	EmailType
	BooleanType
	NumberType
	LocationType
	NestedType
)

func (DataType) MarshalJSON

func (s DataType) MarshalJSON() ([]byte, error)

MarshalJSON marshals the enum as a quoted json string

func (DataType) String

func (t DataType) String() string

func (*DataType) UnmarshalJSON

func (s *DataType) UnmarshalJSON(b []byte) error

UnmarshalJSON unmashals a quoted json string to the enum value

type Form

type Form interface {
	ID() string
	Type() string
	GetSchema() *jtd.Schema
	GetAdditionalBackends() []Backend
	AddBackend(Backend) error
	Validate(Response) bool
}

Form is the generic interface for forms.

type GRPCFormsServiceServer

type GRPCFormsServiceServer struct {
	Service *Service
	formsgrpc.UnimplementedFormsServiceServer
}

func NewGRPCFormServiceServer

func NewGRPCFormServiceServer(s *Service) *GRPCFormsServiceServer

func (*GRPCFormsServiceServer) AddForm

func (*GRPCFormsServiceServer) DeleteForm

func (*GRPCFormsServiceServer) GetForm

func (*GRPCFormsServiceServer) GetFormResponses

func (*GRPCFormsServiceServer) GetForms

func (*GRPCFormsServiceServer) SubmitResponse

type GRPCTransport

type GRPCTransport struct {
	Addr   string
	Server *grpc.Server
}

func NewGRPCTransport

func NewGRPCTransport(addr string) *GRPCTransport

NewRESTTransport creates a new transport using the REST protocol

func (GRPCTransport) Run

func (t GRPCTransport) Run(s *Service)

type KantreeBackend

type KantreeBackend struct {
	BaseUrl    string
	APIKey     string
	ProjectId  string
	TitleField string
}

KantreeBackend is a backend using Kantree

func NewKantreeBackend

func NewKantreeBackend(configuration map[string]interface{}) KantreeBackend

NewKantreeBackend initializes a new empty KantreeBackend

func (KantreeBackend) AddForm

func (b KantreeBackend) AddForm(f Form) error

AddForm creates a new form in the backend

func (KantreeBackend) AddFormBackend

func (b KantreeBackend) AddFormBackend(string, Backend) error

AddFormBackend adds a new backend : unavailable for Kantree backends

func (KantreeBackend) Configuration

func (b KantreeBackend) Configuration() map[string]interface{}

func (KantreeBackend) DeleteForm

func (b KantreeBackend) DeleteForm(formId string) error

DeleteForm removes the form from the backend using formId

func (KantreeBackend) GetForm

func (b KantreeBackend) GetForm(id string) (Form, error)

GetForm retrieves the form from the backend given a formId

func (KantreeBackend) GetFormBackends

func (b KantreeBackend) GetFormBackends(string) ([]Backend, error)

GetFormBackends retrieves form backends : unavailable for Kantree backends

func (KantreeBackend) GetFormResponses

func (b KantreeBackend) GetFormResponses(formId string) ([]Response, error)

GetFormResponses retrieves the responses for a given form (with formId)

func (KantreeBackend) GetForms

func (b KantreeBackend) GetForms() ([]Form, error)

GetForms retrieves all forms from this backend

func (KantreeBackend) SubmitResponse

func (b KantreeBackend) SubmitResponse(form Form, response Response) error

Submitresponse registers a response to the form given it's formid

func (KantreeBackend) Type

func (b KantreeBackend) Type() string

Type returns the backend type (Kantree)

type NoBackend

type NoBackend struct {
}

NoBackend is a fake backend implementation

type NoTransport

type NoTransport struct {
}

NoTransport is a fake transport

func (NoTransport) Run

func (t NoTransport) Run(s *Service)

type PgsqlBackend

type PgsqlBackend struct {
	Conn *pgxpool.Pool
}

PgsqlBackend is a backend using PostgreSQL

func NewPgsqlBackend

func NewPgsqlBackend(databaseurl string) PgsqlBackend

NewPgsqlBackend initializes a new empty PgsqlBackend

func (PgsqlBackend) AddForm

func (b PgsqlBackend) AddForm(f Form) error

AddForm creates a new form in the backend

func (PgsqlBackend) AddFormBackend

func (b PgsqlBackend) AddFormBackend(id string, backend Backend) error

AddFormBackend adds an additional backend to a given form

func (PgsqlBackend) Configuration

func (b PgsqlBackend) Configuration() map[string]interface{}

func (PgsqlBackend) DeleteForm

func (b PgsqlBackend) DeleteForm(formId string) error

DeleteForm removes the form from the backend using formId

func (PgsqlBackend) GetForm

func (b PgsqlBackend) GetForm(id string) (Form, error)

GetForm retrieves the form from the backend given a formId

func (PgsqlBackend) GetFormBackends

func (b PgsqlBackend) GetFormBackends(id string) ([]Backend, error)

func (PgsqlBackend) GetFormResponses

func (b PgsqlBackend) GetFormResponses(formId string) ([]Response, error)

GetFormResponses retrieves the responses for a given form (with formId)

func (PgsqlBackend) GetForms

func (b PgsqlBackend) GetForms() ([]Form, error)

GetForms retrieves all forms from this backend

func (PgsqlBackend) SubmitResponse

func (b PgsqlBackend) SubmitResponse(form Form, response Response) error

Submitresponse registers a response to the form given it's formid

func (PgsqlBackend) Type

func (b PgsqlBackend) Type() string

type RESTBackendDefinition

type RESTBackendDefinition struct {
	StorageType   string                 `json:"storage_type"`
	Configuration map[string]interface{} `json:"configuration, omitempty"`
}

RESTBackendDefinition defines the storage type and backend configuration

Configuration keys depend on the storage type

func RESTBackend

func RESTBackend(b Backend) RESTBackendDefinition

RESTBackend transforms a Backend to the RESTBackendDefinition type for correct JSON marshalling

func (RESTBackendDefinition) Backend

func (b RESTBackendDefinition) Backend() Backend

type RESTFormDefinition

type RESTFormDefinition struct {
	ID                 string                  `json:"id"`
	Type               string                  `json:"type"` // "Unstructured" or "Structured"
	Schema             *jtd.Schema             `json:"schema,omitempty"`
	AdditionalBackends []RESTBackendDefinition `json:"additional_backends,omitempty"`
}

RESTFormDefintion is the form form definition

The type of the form is "Unstructured" if there is no specific schema, or structured. Default value (if unknown or any other value) is "Unstructured".

func RESTForm

func RESTForm(f Form) RESTFormDefinition

type RESTTransport

type RESTTransport struct {
	Addr    string
	Service *Service
}

RESTTransport is the structure for the HTTP REST transport

func NewRESTTransport

func NewRESTTransport(addr string) *RESTTransport

NewRESTTransport creates a new transport using the REST protocol

func (RESTTransport) Run

func (t RESTTransport) Run(s *Service)

Run runs the HTTP server

func (RESTTransport) ServeHTTP

func (t RESTTransport) ServeHTTP(rw http.ResponseWriter, req *http.Request)

ServeHTTP handles HTTP requests

type Response

type Response interface{}

type Service

type Service struct {
	Transports []Transport
	Backends   []Backend
}

Service is the web service for the forms handling. It needs transports to communicate with the outside world, and backends to manage the data

A service can have multiple transports and backends. For data consistency, in case of multiple backends, the first one in the list is the reference. If you mix backends with data retention (database for example) and backends without (email for example), set a backend with data retention as the first one in the list

func NewService

func NewService(transports []Transport, backends []Backend) *Service

NewService creates a new service with transports and backends

func (*Service) AddForm

func (s *Service) AddForm(f Form) error

AddForm creates a new form

func (*Service) AddFormBackend

func (s *Service) AddFormBackend(formid string, b Backend) error

AddFormBackend adds a backend to a form

func (*Service) DeleteForm

func (s *Service) DeleteForm(formId string) error

DeleteForm deletes a form

func (*Service) GetForm

func (s *Service) GetForm(formId string) (Form, error)

GetForm retrieves a single form given its ID

func (*Service) GetFormBackends

func (s *Service) GetFormBackends(formid string) ([]Backend, error)

AddFormBackend adds a backend to a form

func (*Service) GetFormResponses

func (s *Service) GetFormResponses(formId string) ([]Response, error)

GetFormResponses sends a response to a form

func (*Service) GetForms

func (s *Service) GetForms() ([]Form, error)

GetForms retrieves the forms list

func (*Service) Run

func (s *Service) Run()

Run runs the service, launching with backends and transports

func (*Service) SubmitResponse

func (s *Service) SubmitResponse(formId string, response Response) error

SubmitResponse sends a response to a form

type StructuredForm

type StructuredForm struct {
	Schema             *jtd.Schema
	AdditionalBackends []Backend
	// contains filtered or unexported fields
}

StructuredForm is a Form with a defined schema. Submitted data must match the given schema

func NewStructuredForm

func NewStructuredForm(id string, schema *jtd.Schema, backends ...Backend) *StructuredForm

NewStructuredForm creates a new structured form with a given ID and schema

func (*StructuredForm) AddBackend

func (f *StructuredForm) AddBackend(b Backend) error

AddBackend adds a new backend to the form

func (*StructuredForm) GetAdditionalBackends

func (f *StructuredForm) GetAdditionalBackends() []Backend

GetAdditionalBackends returns the additional backends for this form

func (*StructuredForm) GetSchema

func (f *StructuredForm) GetSchema() *jtd.Schema

GetSchema returns the data schema of the form

func (*StructuredForm) ID

func (f *StructuredForm) ID() string

ID returns the structured form ID

func (*StructuredForm) Type

func (f *StructuredForm) Type() string

Type returns the type of the form (Structured)

func (*StructuredForm) Validate

func (f *StructuredForm) Validate(response Response) bool

Validate validates that the response follows the form's schema

type TempBackend

type TempBackend struct {
	Forms     map[string]Form
	Responses map[string][]Response
}

TempBackend is an in-memory temporary backend primarily used for testing and development purposes

func NewTempBackend

func NewTempBackend() TempBackend

NewTempBackend initializes a new empty TempBackend

func (TempBackend) AddForm

func (b TempBackend) AddForm(f Form) error

AddForm creates a new form in the backend

func (TempBackend) AddFormBackend

func (b TempBackend) AddFormBackend(formid string, backend Backend) error

AddFormBackend adds a backend to a form

func (TempBackend) Configuration

func (b TempBackend) Configuration() map[string]interface{}

func (TempBackend) DeleteForm

func (b TempBackend) DeleteForm(formId string) error

DeleteForm removes the form from the backend using formIs

func (TempBackend) GetForm

func (b TempBackend) GetForm(formId string) (Form, error)

GetForm retrieves the form from the backend given a formId

func (TempBackend) GetFormBackends

func (b TempBackend) GetFormBackends(formid string) ([]Backend, error)

GetFormBackends returns all the backends for one form

func (TempBackend) GetFormResponses

func (b TempBackend) GetFormResponses(formId string) ([]Response, error)

func (TempBackend) GetForms

func (b TempBackend) GetForms() ([]Form, error)

GetForms retrieves all forms from this backend

func (TempBackend) SubmitResponse

func (b TempBackend) SubmitResponse(form Form, response Response) error

func (TempBackend) Type

func (b TempBackend) Type() string

type Transport

type Transport interface {
	Run(*Service)
}

Transport is the transport interface for communication with the outside world

type UnstructuredForm

type UnstructuredForm struct {
	AdditionalBackends []Backend
	// contains filtered or unexported fields
}

UnstructuredForm is a form without any given schema. It accepts unstructured data

func NewUnstructuredForm

func NewUnstructuredForm(id string, backends ...Backend) *UnstructuredForm

NewUnstructuredForm creates a new unstructured form with a given id

func (*UnstructuredForm) AddBackend

func (f *UnstructuredForm) AddBackend(b Backend) error

AddBackend adds a new backend to the form

func (*UnstructuredForm) GetAdditionalBackends

func (f *UnstructuredForm) GetAdditionalBackends() []Backend

GetAdditionalBackends returns the additional backends for this form

func (*UnstructuredForm) GetSchema

func (f *UnstructuredForm) GetSchema() *jtd.Schema

GetSchema returns nil because unstructured form do not have schemas

func (*UnstructuredForm) ID

func (f *UnstructuredForm) ID() string

ID returns the ID of the unstructured form

func (*UnstructuredForm) Type

func (f *UnstructuredForm) Type() string

Type returns the type of the form (Unstructured)

func (*UnstructuredForm) Validate

func (f *UnstructuredForm) Validate(response Response) bool

Validate validates that the response follows the form's schema : always true as unstructured form

Directories

Path Synopsis
cmd
formssvc Module

Jump to

Keyboard shortcuts

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