mirror of
https://github.com/acaloiaro/frm
synced 2026-07-21 10:12:23 +00:00
192 lines
5.8 KiB
Go
192 lines
5.8 KiB
Go
package frm
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/acaloiaro/frm/internal"
|
|
)
|
|
|
|
const (
|
|
EventDraftCreated = "frmDraftCreated" // htmx event sent when new drafts are created
|
|
)
|
|
|
|
var ErrCannotDetermineWorkspace = errors.New("workspace cannot be determine without WorkspaceID or WorkspaceIDUrlParam")
|
|
var ErrNoInstanceAvailable = errors.New("no frm instance is available on the context")
|
|
|
|
// Frm is the primary API into frm
|
|
type Frm struct {
|
|
BuilderMountPoint string // relative URL path where frm mounts the builder to your app's router
|
|
CollectorMountPoint string // relative URL path where frm mounts the collector to your app's router
|
|
DBArgs internal.DBArgs // database arguments
|
|
WorkspaceID string // ID of the workspace that frm acts on behalf of
|
|
WorkspaceIDUrlParam string // name of the URL parameter that provides your workspace ID
|
|
}
|
|
|
|
// Args are arguments passed to Frm
|
|
type Args struct {
|
|
BuilderMountPoint string // path on the router to mount frm's builder
|
|
CollectorMountPoint string // path on the router to mount frm's collector
|
|
PostgresDisableSSL bool // disable ssl when connecting to postgres
|
|
PostgresURL string // postgres database URL
|
|
PostgresSchema string // postgres schema where frm stores data
|
|
WorkspaceID string // ID of the workspace for which frm is being initialized
|
|
WorkspaceIDUrlParam string // named URL parameter that identifies the workspace, e.g. for route /{workspace_id}, the value would be "workspace_id"
|
|
}
|
|
|
|
// FormStatus is the status of a Form
|
|
//
|
|
// - Published forms are available to be used
|
|
//
|
|
// - Draft forms are in a draft state, yet to be published
|
|
type FormStatus = internal.FormStatus
|
|
|
|
const FormStatusPublished = internal.FormStatusPublished
|
|
const FormStatusDraft = internal.FormStatusDraft
|
|
|
|
// New initializes a new frm instance
|
|
//
|
|
// If the frm database has not yet been initialized, Init() should be called before mounting to a router
|
|
func New(args Args) (f *Frm, err error) {
|
|
if args.WorkspaceID == "" && args.WorkspaceIDUrlParam == "" {
|
|
return nil, ErrCannotDetermineWorkspace
|
|
}
|
|
|
|
f = &Frm{
|
|
BuilderMountPoint: strings.TrimSuffix(args.BuilderMountPoint, "/"),
|
|
CollectorMountPoint: strings.TrimSuffix(args.CollectorMountPoint, "/"),
|
|
WorkspaceID: args.WorkspaceID,
|
|
WorkspaceIDUrlParam: args.WorkspaceIDUrlParam,
|
|
DBArgs: internal.DBArgs{
|
|
URL: args.PostgresURL,
|
|
DisableSSL: args.PostgresDisableSSL,
|
|
Schema: args.PostgresSchema,
|
|
},
|
|
}
|
|
return
|
|
}
|
|
|
|
// Init initializes the frm database if it hasn't been initialized
|
|
func (f *Frm) Init(ctx context.Context) (err error) {
|
|
err = internal.InitializeDB(ctx, f.DBArgs)
|
|
return
|
|
}
|
|
|
|
// GetForm retrieves forms by ID
|
|
func (f *Frm) GetForm(ctx context.Context, id int64) (form Form, err error) {
|
|
var frm internal.Form
|
|
frm, err = internal.Q(ctx, f.DBArgs).GetForm(ctx, internal.GetFormParams{
|
|
WorkspaceID: f.WorkspaceID,
|
|
ID: id,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
form = (Form)(frm)
|
|
return
|
|
}
|
|
|
|
type ListFormsArgs struct {
|
|
Statuses []FormStatus
|
|
}
|
|
|
|
// ListForms lists all forms for the current workspace
|
|
func (f *Frm) ListForms(ctx context.Context, args ListFormsArgs) (forms []Form, err error) {
|
|
var fs Forms
|
|
statuses := []internal.FormStatus{}
|
|
for _, s := range args.Statuses {
|
|
statuses = append(statuses, (internal.FormStatus)(s))
|
|
}
|
|
fs, err = internal.Q(ctx, f.DBArgs).ListForms(ctx, internal.ListFormsParams{
|
|
WorkspaceID: f.WorkspaceID,
|
|
Statuses: statuses,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, f := range fs {
|
|
forms = append(forms, (Form)(f))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Instance returns the frm instance from the request context
|
|
func Instance(ctx context.Context) (i *Frm, err error) {
|
|
var ok bool
|
|
i, ok = ctx.Value(internal.FrmContextKey).(*Frm)
|
|
if !ok {
|
|
return nil, ErrNoInstanceAvailable
|
|
}
|
|
return
|
|
}
|
|
|
|
type CreateShortCodeArgs struct {
|
|
FormID int64
|
|
SubjectID string
|
|
}
|
|
|
|
// CreateShortCode creates short code for a given form and subject
|
|
func (f *Frm) CreateShortCode(ctx context.Context, args CreateShortCodeArgs) (sc ShortCode, err error) {
|
|
var s internal.ShortCode
|
|
s, err = internal.Q(ctx, f.DBArgs).SaveShortCode(ctx, internal.SaveShortCodeParams{
|
|
WorkspaceID: f.WorkspaceID,
|
|
FormID: &args.FormID,
|
|
ShortCode: internal.GenCode(),
|
|
SubjectID: args.SubjectID,
|
|
})
|
|
return (ShortCode)(s), err
|
|
}
|
|
|
|
// # CollectorPath returns paths to frm collector endpoints
|
|
//
|
|
// It uses the collector's mount point on the router to generate collector paths
|
|
func CollectorPath(ctx context.Context, path string) string {
|
|
base, ok := ctx.Value(internal.CollectorMountPointContextKey).(string)
|
|
if !ok {
|
|
return "/"
|
|
}
|
|
urlPath := filepath.Clean(fmt.Sprintf("%s/%s", base, path))
|
|
return urlPath
|
|
}
|
|
|
|
// BuilderPathForm returns the builder URL path for the provided form ID
|
|
func BuilderPathForm(ctx context.Context, formID int64) string {
|
|
base, ok := ctx.Value(internal.BuilderMountPointContextKey).(string)
|
|
if !ok {
|
|
return "/"
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%d", base, formID)
|
|
}
|
|
|
|
// BuilderPathFormField returns the builder URL path for the provided form ID and field ID
|
|
func BuilderPathFormField(ctx context.Context, formID int64, fieldID string, args ...string) string {
|
|
base, ok := ctx.Value(internal.BuilderMountPointContextKey).(string)
|
|
if !ok {
|
|
return "/"
|
|
}
|
|
|
|
additionalPath := ""
|
|
if len(args) > 0 {
|
|
additionalPath = args[0]
|
|
}
|
|
|
|
path := filepath.Clean(fmt.Sprintf("%s/%d/fields/%s/%s", base, formID, fieldID, additionalPath))
|
|
return path
|
|
}
|
|
|
|
// CollectorPathForm returns the collector URL path for the provided form ID
|
|
func CollectorPathForm(ctx context.Context, formID int64, path ...string) string {
|
|
base, ok := ctx.Value(internal.CollectorMountPointContextKey).(string)
|
|
if !ok {
|
|
return "/"
|
|
}
|
|
base = filepath.Clean(base)
|
|
return fmt.Sprintf("%s/%d", base, formID)
|
|
}
|