gofmcon

package module
v1.0.4 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2023 License: MIT Imports: 11 Imported by: 0

README

Go Report Card

About

This library provides access to FileMaker Server using XML Web publishing.

Initially the library was a port of https://github.com/PerfectlySoft/Perfect-FileMaker, but it evolved a lot since.

In Production

The library is used in production to proxy the calls from API server to FileMaker database.

Tests

FileMaker differs Postgres or MySQL, so we cannot run docker and test the library in CI/CD. Maybe we could run EC2 with Windows and install FileMaker Server on it to run the integration test, yet it seems a bit overkill and very time-consuming at this moment.

Installation

Run the command below in the root directory of your project:

go get github.com/amanbolat/gofmcon

Then import the lib in your code:

import "github.com/amanbolat/gofmcon"

Examples

Full example

package main

import (
	"encoding/json"
	fm "github.com/amanbolat/gofmcon"
	"log"
	"github.com/kelseyhightower/envconfig"
	"fmt"
	"errors"
)

// config represents all the configuration we need in order to
// create a new FMConnector and establish the connection with 
// FileMaker database 
type config struct {
	FmHost         string `split_words:"true" required:"true"`
	FmUser         string `split_words:"true" required:"true"`
	FmPort         string `split_words:"true" required:"true"`
	FmDatabaseName string `split_words:"true" required:"true"`
	FmPass         string `split_words:"true" required:"true"`
}

type postStore struct {
	fmConn *fm.FMConnector
	dbName string
}

type Post struct {
	Author  string `json:"Author"`
	Title   string `json:"Title"`
	Content string `json:"Content"`
}

func (p *Post) Populate(record *fm.Record) {
	p.Author = record.Field("author")
	p.Title = record.Field("title")
	p.Content = record.Field("content")
}

func main() {
	var conf = &config{}
	err := envconfig.Process("", conf)
	if err != nil {
		log.Fatal(err)
	}

	fmConn := fm.NewFMConnector(conf.FmHost, conf.FmPort, conf.FmUser, conf.FmPass)
	store := postStore{fmConn: fmConn, dbName: conf.FmDatabaseName}

	posts, err := store.GetAllPosts(fmConn)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(posts)
}

func (ps *postStore) GetAllPosts() ([]Post, error) {
	var posts []Post

	q := fm.NewFMQuery(ps.dbName, "posts_list_layout", fm.FindAll)
	fmset, err := ps.fmConn.Query(q)
	if err != nil {
		return posts, errors.New("failed to get posts")
	}

	// Populate it with record
	for _, r := range fmset.Resultset.Records {
		p := Post{}

		b, _ := r.JsonFields()
		_ = json.Unmarshal(b, &p)
		posts = append(posts, p)
	}

	return posts, nil
}

Get a single record

    q := fm.NewFMQuery(databaseName, layout_name, fm.Find)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "001", Op: fm.Equal},
    ).Max(1)

Check if the error is FileMaker specific one

    fmSet, err := fmConn.Query(q)
    if err != nil {
        if err.Error() == fmt.Sprintf("FileMaker_error: %s", fm.FileMakerErrorCodes[401]) {
            // your code
        }
    
        // else do something
    }

Create a record

    q := fm.NewFMQuery(databaseName, layout_name, fm.New)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "some_value"},
    )
    
    fmSet, err := fmConn.Query(q)

Sort the records

    q.WithSortFields(fm.FMSortField{Name: "some_field", Order: fm.Descending})

Update a record

Your object should have FileMaker record id to update record in database. Please see more in FileMaker documentation.

    q := fm.NewFMQuery(databaseName, layout_name, fm.Edit)
    q.WithFields(
        fm.FMQueryField{Name: "field_name", Value: "some_new_value"},
    )
    q.WithRecordId(updated_object.FMRecordID)

Run a script

    // SCRIPT_DELIMITER can be '|', '_' or any other symbol that will be
    // parsed on FileMaker side to get all the parameters from the string
    q.WithPostFindScripts(SCRIPT_NAME, strings.Join([]string{param_1, param_2, param_3}, SCRIPT_DELIMITER))

Documentation

Index

Constants

View Source
const (
	// DateFormat is a format of date on a particular layout
	DateFormat = "01/02/2006"
	// TimeFormat is a format of time on a particular layout
	TimeFormat = "15:04:05"
	// TimestampFormat is a format of timestamp on a particular layout
	TimestampFormat = "01/02/2006 15:04:05"
)
View Source
const (

	// FMDBNames adds –dbnames (Database names) query command
	FMDBNames = "-dbnames"
)

Variables

View Source
var FileMakerErrorCodes = map[int]string{}/* 243 elements not displayed */

FileMakerErrorCodes are all error codes taken from FileMaker official documentation

Functions

This section is empty.

Types

type DataSource

type DataSource struct {
	Database        string `xml:"database,attr"`
	DateFormat      string `xml:"date_format,attr"`
	Layout          string `xml:"layout,attr"`
	Table           string `xml:"table,attr"`
	TimeFormat      string `xml:"time-format,attr"`
	TimestampFormat string `xml:"timestamp-format,attr"`
	TotalCount      int    `xml:"total-count,attr"`
}

DataSource store database name, layout name and time formats

type FMAction

type FMAction string

FMAction is a type iof action can be done to the record

const (
	// Find -findquery
	Find FMAction = "-findquery"
	// FindAll -findall
	FindAll FMAction = "-findall"
	// FindAny findany
	FindAny FMAction = "-findany"
	// New -new
	New FMAction = "-new"
	// Edit -edit
	Edit FMAction = "-edit"
	// Delete -delete
	Delete FMAction = "-delete"
	// Duplicate -dup
	Duplicate FMAction = "-dup"
)

func (FMAction) String

func (a FMAction) String() string

type FMConnector

type FMConnector struct {
	Host     string
	Port     string
	Username string
	Password string
	Client   *http.Client
	Debug    bool
}

FMConnector includes all the information about FM database to be able to connect to that

func NewFMConnector

func NewFMConnector(host string, port string, username string, password string) *FMConnector

NewFMConnector creates new FMConnector object

func (*FMConnector) Ping

func (fmc *FMConnector) Ping(ctx context.Context) error

Ping sends a simple request querying all available databases in order to check connection and credentials

func (*FMConnector) Query

func (fmc *FMConnector) Query(ctx context.Context, q *FMQuery) (FMResultset, error)

Query fetches FMResultset from FileMaker server depending on FMQuery given to it

func (*FMConnector) SetDebug added in v0.3.4

func (fmc *FMConnector) SetDebug(v bool)

SetDebug sets debug level of logger to Debug. DON'T use it in production. Your record information can leak to the logs

type FMError

type FMError struct {
	Code int `xml:"code,attr"`
}

FMError represents a FileMaker error

func (*FMError) Error

func (e *FMError) Error() string

func (*FMError) String

func (e *FMError) String() string

type FMFieldOp

type FMFieldOp string

FMFieldOp is type of operator for a FMField

const (
	// Equal -eq
	Equal FMFieldOp = "eq"
	// Contains -cn
	Contains FMFieldOp = "cn"
	// BeginsWith -bw
	BeginsWith FMFieldOp = "bw"
	// EndsWith -ew
	EndsWith FMFieldOp = "ew"
	// GreaterThan -gt
	GreaterThan FMFieldOp = "gt"
	// GreaterThanEqual -gte
	GreaterThanEqual FMFieldOp = "gte"
	// LessThan -lt
	LessThan FMFieldOp = "lt"
	// LessThanEqual -lte
	LessThanEqual FMFieldOp = "lte"
)

type FMLogicalOp

type FMLogicalOp string

FMLogicalOp is a type for logical operators

const (
	// And operator
	And FMLogicalOp = "and"
	// Or operator
	Or FMLogicalOp = "or"
	// Not operator
	Not FMLogicalOp = "not"
)

type FMQuery

type FMQuery struct {
	Database            string
	Layout              string
	Action              FMAction
	QueryFields         []FMQueryFieldGroup
	SortFields          []FMSortField
	RecordID            int // default should be -1
	PreSortScript       string
	PreFindScript       string
	PostFindScript      string
	PreSortScriptParam  string
	PreFindScriptParam  string
	PostFindScriptParam string
	ResponseLayout      string
	ResponseFields      []string
	MaxRecords          int // default should be -1
	SkipRecords         int // default should be 0
	Query               map[string]string
}

FMQuery represent the query you are sending to the server

func NewFMQuery

func NewFMQuery(database string, layout string, action FMAction) *FMQuery

NewFMQuery creates new FMQuery object

func (*FMQuery) Max

func (q *FMQuery) Max(n int) *FMQuery

Max sets maximum amount of records to fetch

func (*FMQuery) QueryString

func (q *FMQuery) QueryString() string

QueryString creates query string based on FMQuery

func (*FMQuery) Skip

func (q *FMQuery) Skip(n int) *FMQuery

Skip skips n amount of recrods

func (*FMQuery) WithFieldGroups

func (q *FMQuery) WithFieldGroups(fieldGroups ...FMQueryFieldGroup) *FMQuery

WithFieldGroups sets groups of fields for find request

func (*FMQuery) WithFields

func (q *FMQuery) WithFields(fields ...FMQueryField) *FMQuery

WithFields sets field for the find request

func (*FMQuery) WithPostFindScript

func (q *FMQuery) WithPostFindScript(script, param string) *FMQuery

WithPostFindScript sets PostFindScript script and params

func (*FMQuery) WithPreFindScript

func (q *FMQuery) WithPreFindScript(script, param string) *FMQuery

WithPreFindScript sets PreFindScript and params

func (*FMQuery) WithPreSortScript

func (q *FMQuery) WithPreSortScript(script, param string) *FMQuery

WithPreSortScript sets PreSortScript and params

func (*FMQuery) WithRecordID added in v0.3.5

func (q *FMQuery) WithRecordID(id int) *FMQuery

WithRecordID sets RecordID for the query. MUST have if you want to edit/delete record

func (*FMQuery) WithResponseFields

func (q *FMQuery) WithResponseFields(fields ...string) *FMQuery

WithResponseFields adds field names that FileMaker server should return

func (*FMQuery) WithResponseLayout

func (q *FMQuery) WithResponseLayout(lay string) *FMQuery

WithResponseLayout sets layout name you want to fetch records from

func (*FMQuery) WithSortFields

func (q *FMQuery) WithSortFields(sortFields ...FMSortField) *FMQuery

WithSortFields sets sort fields' name and order

type FMQueryField

type FMQueryField struct {
	Name  string
	Value string
	Op    FMFieldOp
}

FMQueryField is a field used in FMQuery

type FMQueryFieldGroup

type FMQueryFieldGroup struct {
	Op     FMLogicalOp
	Fields []FMQueryField
}

FMQueryFieldGroup groups of fields used for the find request

type FMResultset

type FMResultset struct {
	Resultset  *Resultset  `xml:"resultset"`
	DataSource *DataSource `xml:"datasource"`
	MetaData   *MetaData   `xml:"metadata"`
	Version    string      `xml:"version,attr"`
	FMError    FMError     `xml:"error"`
}

FMResultset is a collection of ResultSets

func (*FMResultset) HasError

func (rs *FMResultset) HasError() bool

HasError checks if FMResultset was fetched with an error

type FMSortField

type FMSortField struct {
	Name  string
	Order FMSortOrder
}

FMSortField is a field that should be sorted during the query

type FMSortOrder

type FMSortOrder string

FMSortOrder is a type of order

const (
	// Ascending -ascend
	Ascending FMSortOrder = "ascend"
	// Descending -descend
	Descending FMSortOrder = "descend"
	// Custom -custom
	Custom FMSortOrder = "custom"
)

func (FMSortOrder) String

func (so FMSortOrder) String() string

type Field

type Field struct {
	Name string   `xml:"name,attr" json:"field"`
	Data []string `xml:"data" json:"data"`
	Type FieldType
}

Field stands for field in a record

type FieldDefinition added in v0.3.0

type FieldDefinition struct {
	Name          string `xml:"name,attr"`
	AutoEnter     bool
	FourDigitYear bool
	Global        bool
	MaxRepeat     int
	NotEmpty      bool
	NumericOnly   bool
	TimeOfDay     bool
	Type          FieldType
}

FieldDefinition store information about a field in given layout

func (*FieldDefinition) UnmarshalXML added in v0.3.0

func (f *FieldDefinition) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML serializes xml data of FieldDefinition into the object

type FieldType added in v0.3.0

type FieldType string

FieldType represents a type of the field

const (
	// TypeText is a text type of the field
	TypeText FieldType = "text"
	// TypeNumber is a number type of the field
	TypeNumber FieldType = "number"
	// TypeDate is a date type of the field
	TypeDate FieldType = "date"
	// TypeTime is a time type of the field
	TypeTime FieldType = "time"
	// TypeTimestamp is a timestamp type of the field
	TypeTimestamp FieldType = "timestamp"
	// TypeContainer is a container type of the field
	TypeContainer FieldType = "container"
)

type FieldsDefinitions added in v0.3.0

type FieldsDefinitions []FieldDefinition

FieldsDefinitions is type of []FieldDefinition

type MetaData added in v0.3.0

type MetaData struct {
	FieldDefinitions     []*FieldDefinition    `xml:"field-definition"`
	RelatedSetDefinition *RelatedSetDefinition `xml:"relatedset-definition"`
}

MetaData store fields' and related sets' meta information

type Record

type Record struct {
	ID     int      `xml:"record-id,attr"`
	Fields []*Field `xml:"field"`

	RelatedSet []*RelatedSet `xml:"relatedset"`
	// contains filtered or unexported fields
}

Record is FileMaker record

func (*Record) Field

func (r *Record) Field(name string) interface{}

Field returns fields data for given field name

func (*Record) JSONFields added in v0.3.5

func (r *Record) JSONFields() ([]byte, error)

JSONFields return JSON representation of Record

func (*Record) RelatedSetFromTable

func (r *Record) RelatedSetFromTable(t string) *RelatedSet

RelatedSetFromTable returns the set of related records from given related table

type RelatedSet

type RelatedSet struct {
	Count   int       `xml:"count,attr"`
	Table   string    `xml:"table,attr"`
	Records []*Record `xml:"record"`
}

RelatedSet is a set of records returned from FileMaker database

type RelatedSetDefinition added in v0.3.0

type RelatedSetDefinition struct {
	Table            string             `xml:"table,attr"`
	FieldDefinitions []*FieldDefinition `xml:"field-definition"`
}

RelatedSetDefinition is meta information of related set

type Resultset

type Resultset struct {
	Count   int       `xml:"count,attr" json:"count"`
	Fetched int       `xml:"fetch-size,attr" json:"fetched"`
	Records []*Record `xml:"record"`
}

Resultset is a set of records with meta information

Jump to

Keyboard shortcuts

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