common

package
v0.0.0-...-646b7d3 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2021 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package common holds structures and data which are common across all databases and architectures (JS / Server). This exists because it is often helpful to translate information from several sources into one intermediate form and then translate it back out again.

Note: pointerConvert is needed so we know if a field is the zero value (like "") or missing (like nil). This is the same strategy used by some google data transport libraries written in Go, and also used by Stripe to know if a field is supposed to be the zero value or nil.

Index

Constants

View Source
const (
	// CollectionData holds all customer-owned data
	CollectionData = "data"
	// CollectionSchema holds all data defining CollectionData's schema
	CollectionSchema = "schema"
	// CollectionUser holds all data for particular users
	CollectionUser = "user"
	// CollectionRole holds document with authorization information
	CollectionRole = "role"
	// CollectionUX holds data which helps define a piece of UI and how users interact with it
	CollectionUX = "ux"
	// CollectionTask holds all data which helps bind together Schemed Data for a User in a UX to accomplish some goals
	CollectionTask = "task"
	// CollectionBot is where Bot definitions are stored.
	CollectionBot = "bot"
	// CollectionAction is where Action definitions are stored.
	CollectionAction = "action"
	// CollectionPublicMap contains information which helps route and respond to incoming requests from other domains.
	CollectionPublicMap = "publicmap"
	// CollectionCSS contains information related to CSS on websites hosted by Polyapp.
	CollectionCSS = "css"
)
View Source
const (
	// PolyappIndustryID is a field which holds the Industry
	PolyappIndustryID = "polyappIndustryID"
	// PolyappDomainID is a field which holds the Domain
	PolyappDomainID = "polyappDomainID"
	// PolyappSchemaID is a field which holds the this grouping of information under the Domain; aka a Schema.
	PolyappSchemaID = "polyappSchemaID"
	// PolyappDeprecated is a field which holds whether or not this Document is Deprecated
	PolyappDeprecated = "polyappDeprecated"
	// PolyappFirestoreID is a field in non-Firestore databases which holds the FirestoreID.
	PolyappFirestoreID = "polyappFirestoreID"
)
View Source
const (
	TemplateUserDataID = "EtCKtxdfmz8UmmiGjgaO"
	TemplateUserUserID = "p3gP9Lrox8pBxqQdeFUH"
	HomeTaskID         = "NrCEAuHUenfkPKXrwafnbwPKx"
	HomeUXID           = "GfzLPQfRAFMPjKgiRCQmMoCDK"
	HomeSchemaID       = "BJVHCgzBvaahJFBMOGTZSniwK"
	HomeDataID         = "lBDjYHsEaKDJAbuWSotncPIPb"
)

Variables

View Source
var (
	BrowserActionOpenNewTab = "Open New Tab"
)
View Source
var (
	ErrTaskGoalEvaluateNotHardCoded = errors.New("Task Goal Evaluate Not Hard Coded")
)
View Source
var IterDone = errors.New("no more items in iterator")

IterDone is returned by an iterator's Next method when the iteration is complete; when there are no more items to return.

Functions

func ABToAInterface

func ABToAInterface(A []bool) []interface{}

func ABytesToAInterface

func ABytesToAInterface(A [][]byte) []interface{}

func AFToAInterface

func AFToAInterface(A []float64) []interface{}

func AIToAInterface

func AIToAInterface(A []int64) []interface{}

func ASToAInterface

func ASToAInterface(AS []string) []interface{}

func AddFieldPrefix

func AddFieldPrefix(industryID string, domainID string, schemaID string, k string) string

AddFieldPrefix converts a key of the format: "key" to the format: "industry_context_schema_key".

func BlobURL

func BlobURL(firestoreID string, field string) string

BlobURL returns the URL of a blob asset like an image or audio file given that asset's ID in Firestore. An example ID would be: industry_domain_schema_field%20name

Note: this is coupled to BlobHandler in defaultservice/main.go

func Bool

func Bool(v bool) *bool

Bool is a helper routine that allocates a new bool value to store v and returns a pointer to it.

func BytesToAInterface

func BytesToAInterface(A []byte) []interface{}

func CSSEscape

func CSSEscape(id string) string

CSSEscape takes a sequence of characters which could form a valid HTML5 ID and translates them to a sequence of characters which forms a valid HTML4 ID. It does this primarily by escaping characters like leading numbers and !"@#(*)$%, etc.

Reference documentation: https://mathiasbynens.be/notes/css-escapes

Please remember that this does NOT convert invalid characters like ' ' into something like %20. So typically you would call url.PathEscape(id) and then CSSEscape(id).

func CloseLogging

func CloseLogging()

CloseLogging closes the logging connections and flushes all of the logs out.

func ConvertDataToMatchSchema

func ConvertDataToMatchSchema(data *Data, schema *Schema) error

ConvertDataToMatchSchema finds keys which should be Ints which are currently floats and converts them and finds key which should be Floats which are currently Ints and converts them.

To determine the proper data type, schema is examined.

This should really be called whenever you are retrieving Data from the database but under some circumstances you don't have access to the Schema for a Data so you won't be able to call this. When this is called, you should also call RemoveExtraFields.

func CreateRef

func CreateRef(IndustryID string, DomainID string, TaskID string, UXID string, SchemaID string, DataID string) string

CreateRef is an alias for CreateURL. It forms a URL which is valid as a Ref in common.Data documents.

An example output is: /t/asdlkjad/adslkj/BCiXCONsZuDxjTYEwNOonlLZc?ux=BCiXCONsZuDxjTYEwNOonlLZc&schema=BCiXCONsZuDxjTYEwNOonlLZc&data=BCiXCONsZuDxjTYEwNOonlLZc&user=&role=

func CreateURL

func CreateURL(IndustryID string, DomainID string, TaskID string, UXID string, SchemaID string, DataID string, UserID string, RoleID string,
	OverrideIndustryID string, OverrideDomainID string, additionalQueryString ...string) string

CreateURL returns a formed URL based on the information provided. polyappNone is converted to "" before use.

An example output is: /t/asdlkjad/adslkj/BCiXCONsZuDxjTYEwNOonlLZc?ux=BCiXCONsZuDxjTYEwNOonlLZc&schema=BCiXCONsZuDxjTYEwNOonlLZc&data=BCiXCONsZuDxjTYEwNOonlLZc&user=&role=

additionalQueryString lets you add arbitrary strings to the end of the URL.

func CreateURLFromRequest

func CreateURLFromRequest(request POSTRequest) string

CreateURLFromRequest translates a POSTRequest into a string URL.

func DataIntoStructure

func DataIntoStructure(data *Data, ptrToStructure interface{}) error

DataIntoStructure tries to find data elements which have attributes matching tags in the provided structure.

Relevant tags should be formatted like this: `suffix:"Name"`

If no tag is provided, it is assumed the field suffix the exact value so there is no accounting for spaces.

String types will turn data.S and data.Ref into string; and data.AS and data.ARef into []string. Therefore if you have an embedded structure, like a Subtask, you must use the raw ref to allDB.ReadData and do something with it outside this function. This exclusion improves this function's performance and simplifies its purpose.

Special cases: any field with tag PolyappFirestoreID gets data.FirestoreID set into its value. Similar cases: PolyappSchemaID, PolyappDomain, PolyappIndustry

func DataSatisfiesSchema

func DataSatisfiesSchema(d *Data, s *Schema) error

DataSatisfiesSchema if and only if Data's fields are a subset of or == the Schema's fields.

func DataToPOSTData

func DataToPOSTData(data *Data, request POSTRequest) (map[string]map[string]interface{}, error)

DataToPOSTData converts a *common.Data into the map[string]map[string]interface{} Data which is used in POSTRequest as field "Data" and in POSTResponse as field "DataUpdates".

TODO this function only works on Head Data. Make it work on child Data (subtask Data) too.

func Dereference

func Dereference(simple map[string]interface{})

Dereference dereferences pointer values. For instance, it will turn *string values into string.

func FixPOSTRequest

func FixPOSTRequest(r *POSTRequest) error

FixPOSTRequest modifies a passed in POSTRequest. It does NOT handle retrieving the auth header.

Call this to deal with special characters in the URL and trailing slashes and such.

func Float32

func Float32(v float32) *float32

Float32 is a helper routine that allocates a new float32 value to store v and returns a pointer to it.

func Float64

func Float64(v float64) *float64

Float64 is a helper routine that allocates a new float64 value to store v and returns a pointer to it.

func GetCloudProvider

func GetCloudProvider() string

GetCloudProvider returns "" for no cloud or "GOOGLE" for Google Cloud.

func GetCryptographicallyRandString

func GetCryptographicallyRandString(size int) (string, error)

GetCryptographicallyRandString always returns a cryptographically random string of length >= size.

For a faster version which guarantees the string length, use GetRandString(n int).

func GetDataRef

func GetDataRef(request POSTRequest) string

GetDataRef is the first key in AllData. This func should stay in sync with getDataRef on the client.

func GetDeploymentID

func GetDeploymentID() string

GetDeploymentID return "LocalDeployment" for no cloud or the ID of the deployment.

func GetElasticsearchClientAddresses

func GetElasticsearchClientAddresses() []string

GetElasticsearchClientAddresses returns an HTTPS endpoint for an Elasticsearch client.

func GetElasticsearchClientPassword

func GetElasticsearchClientPassword() string

GetElasticsearchClientPassword returns the Elasticsearch Client Password.

func GetElasticsearchClientUsername

func GetElasticsearchClientUsername() string

GetElasticsearchClientUsername returns the Elasticsearch Client Username.

func GetGoogleApplicationCredentials

func GetGoogleApplicationCredentials() string

GetGoogleApplicationCredentials returns the GOOGLE_APPLICATION_CREDENTIALS variable.

This exists to confine os.Getenv calls to this file.

func GetGoogleProjectID

func GetGoogleProjectID() string

GetGoogleProjectID returns the GOOGLE_PROJECT_ID environment variable.

This exists to confine os.Getenv calls to this file.

func GetHubURL

func GetHubURL() string

func GetLastBlobBucket

func GetLastBlobBucket() string

GetLastBlobBucket returns the name of the Google Cloud Bucket / S3 container / etc. which was used to store blobs in the last environment. Ex. 1: Dev bucket is returned when in Staging. Ex. 2: Staging bucket is returned in Prod.

This is used during environment installation. Blobs in the last environment may be copied over into the new environment.

func GetLogger

func GetLogger(serviceName string, serviceVersion string) (io.Writer, error)

GetLogger populates the logging clients and contexts. It also gives you a Writer you can use for error logging.

After calling this, you MUST call 'defer CloseLogging()'

func GetPublicPath

func GetPublicPath() string

GetPublicPath returns the path to the 'public' directory in this git repo.

func GetRandString

func GetRandString(n int) string

GetRandString returns a random string of length n with an intermediate level of entropy. https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go#31832326

func Int

func Int(v int) *int

Int is a helper routine that allocates a new int32 value to store v and returns a pointer to it, but unlike Int32 its argument value is an int.

func Int32

func Int32(v int32) *int32

Int32 is a helper routine that allocates a new int32 value to store v and returns a pointer to it.

func Int64

func Int64(v int64) *int64

Int64 is a helper routine that allocates a new int64 value to store v and returns a pointer to it.

func IsTimeField

func IsTimeField(key string) bool

IsTimeField checks if a field's name is maybe a date or time field stored as a UNIX timestamp.

In addition to calling this function, you should also use this logic on the field value:

_, isTimeField := toSet.(int64)

func LogError

func LogError(errComingIn error)

LogError logs the error to the main logger.

func RemoveDataKeyUnderscore

func RemoveDataKeyUnderscore(k string) string

RemoveDataKeyUnderscore converts a key of the format: "_industry_context_schema_key" to the format: "industry_context_schema_key".

func RemoveExtraFields

func RemoveExtraFields(data *Data, schema *Schema) error

RemoveExtraFields from Data. "Extra Fields" are fields which are present in Data but not present in the Schema of this Data. Extra fields may be introduced when a field is removed from a Task.

This function should always be called after func ConvertDataToMatchSchema or else some data which could have been rectified may be deleted.

func RemoveFieldPrefix

func RemoveFieldPrefix(k string) string

RemoveFieldPrefix converts a key of the format: "industry_context_schema_key" to the format: "key".

func RemoveNonAlphaNumeric

func RemoveNonAlphaNumeric(s string) string

RemoveNonAlphaNumeric removes non-alphanumeric characters from the string passed in it leaves in spaces in the middle but removes them at the edges with strings.TrimSpace

func ReplaceIndustryDomainSchemaInKey

func ReplaceIndustryDomainSchemaInKey(key string, newIndustry, newDomain, newSchema string) string

func RoleHasAccess

func RoleHasAccess(role *Role, industryID string, domainID string, schemaID string, accessType byte) (bool, error)

RoleHasAccess returns true if the role has access to both the industry and domain.

accessType must be one of: 'c', 'r', 'u', 'd'

func RoleHasAccessKeyValues

func RoleHasAccessKeyValues(role *Role, industryID string, domainID string, schemaID string, key string, value string) (bool, error)

RoleHasAccessKeyValues checks if a role has the requested cast-insensitive value at the given case-sensitive key for an Access which includes the Industry and Domain and (optionally) Schema. Returns true, nil if access is allowed.

func SchemaSatisfiesTask

func SchemaSatisfiesTask(s *Schema, t *Task) error

SchemaSatisfiesTask ensures all of a Task's inputs are available from a particular Schema.

func SchemaSatisfiesUX

func SchemaSatisfiesUX(s *Schema, ux *UX) error

SchemaSatisfiesUX ensures a Schema and UX are compatible We *could* go through every HTML ID in UX.HTML and ensure it has something in Schema & pull additional Schemas recursively. But we do not because that would be a lot of work to verify that the "SchemaID" variables are not lying.

func SplitField

func SplitField(field string) (industry, domain, schema, key string)

func String

func String(v string) *string

String is a helper routine that allocates a new string value to store v and returns a pointer to it.

func StructureIntoData

func StructureIntoData(industry string, domain string, schema string, ptrToStructure interface{}, data *Data) error

StructureIntoData is the reverse of DataIntoStructure: it takes data in a structure and places it into *common.Data. Fields in Data need to be prefixed with industry_domain_schema_ so those arguments to this function are required.

structure must be a pointer to a structure where we can get the data to retrieve.

data must have already called Init().

func TaskGoalEvaluate

func TaskGoalEvaluate(key string, task *Task, data *Data) (errorMessage string, err error)

TaskGoalEvaluate returns errorMessage "" if the TaskGoal passes validation, an errorMessage string, or an error in failures.

func Uint32

func Uint32(v uint32) *uint32

Uint32 is a helper routine that allocates a new uint32 value to store v and returns a pointer to it.

func Uint64

func Uint64(v uint64) *uint64

Uint64 is a helper routine that allocates a new uint64 value to store v and returns a pointer to it.

func ValidateCollection

func ValidateCollection(c string) (err error)

ValidateCollection validates that the collection string passed in is one of the known collections

func ValidateRef

func ValidateRef(ref string) error

ValidateRef ensures something which ought to be a Ref type follows the format of a Ref type.

Types

type Access

type Access struct {
	// Industry is the Industry the Datas being granted access to are in.
	Industry string
	// Domain is the Domain of the Datas being granted access to.
	Domain string
	// Schema (optional) of the Datas being granted access to.
	// If Schema is not set or is empty then this Access grants access to all Schemas in the Industry/Domain combination.
	Schema string
	// Create access allows a user to "Create" a new Data, usually with the "Create Data" button.
	Create bool
	// Read access allows a user to perform GET requests with the Data, but never POST or similar requests.
	Read bool
	// Update access allows a user to edit an existing Data.
	Update bool
	// Delete access allows a user to delete a Data.
	Delete bool
	// KeyValue grants custom access control attributes. For example, "EditTask": true grants access to using the Edit
	// Task Bot.
	KeyValue map[string]string `suffix:"Key Value"`
}

Access defines what Industry, Domain, and (optionally) Schema it grants access to and which privileges are granted.

type Action

type Action struct {
	FirestoreID string `json:"polyappFirestoreID"`
	// Name describes this Action
	Name string
	// HelpText explains why this action exists and what it does.
	HelpText   string
	Deprecated *bool `json:"polyappDeprecated"`
}

Action is a name of a function or a chromedp.Action call. Actions are executed with RunBot(bot, data)

Functions are of the format: somefuncname()

chromedp.Action calls are of the format: chromedp.Action

TODO implement the logic needed to get chromedp.Action calls to work. TODO every action a human can take should be doable by a chromedp.Action call, so why not have each action taken translated first into a chromedp.Action and stored in a big list of actions. Periodically updates are transmitted, and then that chromedp.Action list is replayed to apply the changes to the system before it's committed. If things were done this way it might be easier to sync the idbCRUD and firestoreCRUD databases. Doing things this way would also make regression testing really easy. Just replay the chromedp.Actions using a Bot & verify the Data at the end is the same.

func (*Action) CollectionName

func (a *Action) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Action) GetFirestoreID

func (a *Action) GetFirestoreID() string

GetFirestoreID returns the FirestoreID property.

func (*Action) Init

func (a *Action) Init(simple map[string]interface{}) error

Init populates the Action data structure from the map.

func (*Action) Simplify

func (a *Action) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*Action) Validate

func (a *Action) Validate() error

Validate returns an error for improper data.

type Bot

type Bot struct {
	FirestoreID string `json:"polyappFirestoreID"`
	Name        string
	HelpText    string `suffix:"Help Text"`
	// Actions are of type Action. This array contains an ordered list of IDs of Actions to execute.
	ActionIDs  []string `suffix:"Action IDs"`
	Deprecated *bool    `json:"polyappDeprecated"`
}

Bot is a software bot. It is an ordered list of Actions.

func (*Bot) CollectionName

func (b *Bot) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Bot) GetFirestoreID

func (b *Bot) GetFirestoreID() string

GetFirestoreID returns the FirestoreID element.

func (*Bot) Init

func (b *Bot) Init(simple map[string]interface{}) error

Init the Bot with the simple map.

func (*Bot) Simplify

func (b *Bot) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*Bot) Validate

func (b *Bot) Validate() error

Validate returns an error for improper data.

type BrowserAction

type BrowserAction struct {
	Name string // Name of the action to initiate. One of: BrowserActionOpenNewTab, ...
	Data string // Data which modifies how the browser action works. At some point this should be a map[string]interface{} instead.
}

BrowserAction initiates some Javascript which performs things which aren't doable with ModDOMs, like opening a new browser tab.

func (BrowserAction) Validate

func (b BrowserAction) Validate() error

Validate ensures the values in BrowserAction are valid

type CSS

type CSS struct {
	FirestoreID string `json:"polyappFirestoreID"`
	// Tags are search terms used to identify CSS. It could be the name of the CSS, describe what it looks like, or categorize it somehow.
	// The first Tag is always the "Name" of the CSS.
	Tags []string
	// CompiledCSS is the output from the compilation process. It is served directly to end users.
	CompiledCSS *string
	Deprecated  *bool `json:"polyappDeprecated"`
}

CSS contains CSS which is unique to certain pages or websites. This is intended to be used to 'theme' public websites.

func (*CSS) CollectionName

func (c *CSS) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*CSS) GetFirestoreID

func (c *CSS) GetFirestoreID() string

GetFirestoreID returns the FirestoreID property.

func (*CSS) Init

func (c *CSS) Init(simple map[string]interface{}) error

Init CSS with the simple map.

func (*CSS) Simplify

func (c *CSS) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*CSS) Validate

func (c *CSS) Validate() error

Validate returns an error for improper data.

type Data

type Data struct {
	FirestoreID string `json:"polyappFirestoreID"`
	IndustryID  string `json:"polyappIndustryID" firestore:"polyappIndustryID"`
	DomainID    string `json:"polyappDomainID" firestore:"polyappDomainID"`
	SchemaID    string `json:"polyappSchemaID" firestore:"polyappSchemaID"`
	Deprecated  *bool  `json:"polyappDeprecated" firestore:"polyappDeprecated"`
	// SchemaCache is the Schema of this Data. The Schema can't change after the Data has been read, so
	// it should be safe to cache it. Schema is extremely useful for BackPopulate and similar functions.
	SchemaCache Schema `json:"-"`
	// Ref is used for complicated data structures where you need objects within objects. Its format is that of a link:
	//
	// /[industry]/[domain]/[task]?ux=[UXID]&schema=[SchemaID]&data=[dataID]
	//
	// You can create links with common.CreateRef().
	//
	// There are two ways to display a Ref to a user. One is to display the link within an <a> element.
	// The other renders the content of the task as an embedded Task. The decision on which to do is taken by the UX object.
	// See the UX struct for more documentation on this issue.
	//
	// Note: although Refs are stored with an _ prefix in Schema, inside Data they are stored without the _ prefix.
	Ref map[string]*string
	// S is string data
	S map[string]*string
	// I is int data. Uses 64 bits even though JS only does some operations on 32-bit integers
	I map[string]*int64
	// B is boolean data.
	B map[string]*bool
	// F is floating point numbers (number in JS)
	F map[string]*float64
	// ARef is array of Ref. See Ref's description for help.
	ARef map[string][]string
	// AS is array of strings
	AS map[string][]string
	// AI is array of ints
	AI map[string][]int64
	// AB is array of bools
	AB map[string][]bool
	// AF is array of floats
	AF map[string][]float64
	// Bytes is an array of bytes. It is stored into an object whose name is: SchemaName_FirestoreID_Key
	Bytes map[string][]byte
	// ABytes is an array of blobs. It is stored into an object whose name is: SchemaName_FirestoreID_Key_index.
	ABytes map[string][][]byte
	// Deprecated. To delete a field, set its value to nil.
	Nils map[string]bool
}

Data holds the values people enter when filling out forms. It can be difficult to interact with directly because you need to know, for a given key, what data type it is. This 'data type definition' is provided by type Schema. I have previously tried to leave this data structure inside a more generic map, but I found that to be even more difficult to use.

IndustryID, DomainID and SchemaID provide context. For right now, SchemaID is often set to the ID of the schema.

func CreateDataFromSchema

func CreateDataFromSchema(schema *Schema, overrideData *Data) (*Data, error)

CreateDataFromSchema is very helpful if you are trying to load a new page with a new Data. It is used when you take an action in the UI which creates a new Data document which users then immediately navigate to.

overrideData does NOT copy the override's Industry, Domain or Schema IDs. It only copies over the actual data and only if that data's Field is present in the Schema which is also being passed in.

Side effects: This function creates the Data in the DB.

func SchemaConvert

func SchemaConvert(newSchema Schema, data Data, convertKey map[string]string) (Data, error)

SchemaConvert translates a Data document which satisfies oldSchema into a Data document which satisfies NewSchema and returns the converted Data document *without a FirestoreID*. The original Data document is untouched.

There are 3 possibilities for Fields in the Data document. (1) The field is present in the new schema. In this case the field is copied into the new schema. (2) The field is not present in the new schema at the correct DataType. In this case the "convertKey" map is checked. If the field is present as a key & the value of the key is present at convertKey[field] & the the type of the old and new keys match then the field is added to the new Data. (3) The field is invalid or can't be converted. In this case the field is not included in the new Data.

func (*Data) CollectionName

func (d *Data) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Data) GetABool

func (d *Data) GetABool(suffix string) []bool

GetABool returns nil if the key is nil; a valid []bool otherwise

func (*Data) GetABytes

func (d *Data) GetABytes(suffix string) [][]byte

GetABytes returns nil if the key is nil; a valid [][]byte otherwise

func (*Data) GetAFloat

func (d *Data) GetAFloat(suffix string) []float64

GetAFloat returns nil if the key is nil; a valid []float otherwise

func (*Data) GetAInt

func (d *Data) GetAInt(suffix string) []int64

GetAInt returns nil if the key is nil; a valid []int otherwise

func (*Data) GetARef

func (d *Data) GetARef(suffix string) []string

GetARef returns nil if the key is nil; a valid []string otherwise

func (*Data) GetAString

func (d *Data) GetAString(suffix string) []string

GetAString returns nil if the key is nil; a valid []string otherwise

func (*Data) GetBool

func (d *Data) GetBool(suffix string) *bool

GetBool returns nil if the key is nil; a valid bool otherwise

func (*Data) GetBytes

func (d *Data) GetBytes(suffix string) []byte

GetBytes returns nil if the key is nil; a valid []byte otherwise

func (*Data) GetFirestoreID

func (d *Data) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*Data) GetFloat

func (d *Data) GetFloat(suffix string) *float64

GetFloat returns nil if the key is nil; a valid float otherwise

func (*Data) GetInt

func (d *Data) GetInt(suffix string) *int64

GetInt returns nil if the key is nil; a valid int otherwise

func (*Data) GetRef

func (d *Data) GetRef(suffix string) *string

GetRef returns nil if the key is nil; a valid string otherwise

func (*Data) GetString

func (d *Data) GetString(suffix string) *string

GetString returns nil if the key is nil; a valid string otherwise

func (*Data) Init

func (d *Data) Init(simple map[string]interface{}) error

Init makes all of the maps and initializes them. If the inputs are non-nil, it populates DataTypes from the map[string]interface{} structure. If the input key exists and its value is nil the key is stored in Nils.

Strings are assumed to be References if their keys are prefixed with "_".

Keys are assumed to be PathEscaped, so for instance "%20" is unescaped to " ". The opposite of this operation is performed when the UX is created with encodings in its IDs - aka the opposite should already exist in the database.

func (*Data) Merge

func (d *Data) Merge(toMerge *Data) error

Merge merges a Data 'toMerge' into the pointer receiver. It requires that they have the same FirestoreID.

Merging basically creates a superset of the inputs. If you want to remove a value then set its key into the Nils in toMerge. When this function finds keys in the Nils category it searches the d *Data & replaces its value appropriately. Conversely if a value is set in the Nils in d *Data but has a real value in toMerge *Data, the Nils entry is removed. By removing the Nils entry we ensure the new value from toMerge overwrites the null value in d *Data.

func (*Data) Simplify

func (d *Data) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

Caveat: If Nils is set for a key k, & any other map is set at k, then Nils will take precedence. This enables deletion.

func (*Data) Validate

func (d *Data) Validate() error

Validate returns an error for improper data.

type DataTableRequest

type DataTableRequest struct {
	// IndustryID of the request.
	IndustryID string `json:"industry"`
	// DomainID of the request.
	DomainID string `json:"domain"`
	// SchemaID of the request.
	SchemaID string `json:"schema"`
	// Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side processing requests are
	// drawn in sequence by DataTables (Ajax requests are asynchronous and thus can return out of sequence).
	// This is used as part of the draw return parameter (see below).
	Draw int `json:"draw"`
	// Paging first record indicator. This is the start point in the current data set (0 index based - i.e. 0 is the first record).
	Start int `json:"start"`
	// Number of records that the table can display in the current draw. It is expected that the number of records
	// returned will be equal to this number, unless the server has fewer records to return. Note that this can be -1 to
	// indicate that all records should be returned (although that negates any benefits of server-side processing!).
	Length int `json:"length"`
	// format: order[i][column] where i is the 0-start index of the column.
	// Column to which ordering should be applied for the i-th column. This is an index reference to the columns array of information that is also submitted to the server.
	OrderColumn map[int]int
	// format: order[i][dir] where i is the 0-start index of the column.
	// Ordering direction for the i-th column. It will be asc or desc to indicate ascending ordering or descending ordering, respectively.
	OrderDir map[int]string
	// format: columns[i][name] where i is the 0-start index of the column.
	// Column's name, as defined by columns.name.
	ColumnsName []string
	// format: columns[i][orderable] where i is the 0-start index of the column.
	// Flag to indicate if this column is orderable (true) or not (false). This is controlled by columns.orderable.
	ColumnsOrderable []bool
	// LastDocumentID is a set of custom query parameters created to hold the last row of data currently visible on screen.
	// This is supposed to be used by Firestore to enable pagination.
	LastDocumentID string
	// RequiredColumns is an array of IDs of columns whose values must be present for a row to be visible.
	RequiredColumns []string
	// SearchInput is the input to the search box. Searching is handled as part of the AllDBQuery when you do a GET
	// request for DataTables. See that function for more information.
	//
	// TODO support regex, smart, and caseInsensitive options not being false (right now all must be false).
	SearchInput string
	// InitialSearch (optional) if set when the page loads this value becomes the initial value sent up in the GET
	// request for SearchInput.
	InitialSearch string
	// InitialOrderColumn (optional) if set when the page loads this value becomes the initial value sent up in the GET
	// request for OrderColumn (sort of - this sets the column # being sorted).
	InitialOrderColumn string
	// InitialLastDocumentID (optional) if set when the page loads this value becomes the initial value sent up in the GET
	// request for LastDocumentID.
	InitialLastDocumentID string
}

DataTableRequest is based on https://datatables.net/manual/server-side.

Features turned on/off can be found when DataTables is initialized: $(headElement).find('.polyappDataTable').DataTable

Features which are on and we need handling for on the server: lengthChange, ordering, paging, serverSide.

func CreateDataTableRequest

func CreateDataTableRequest(URL url.URL) (DataTableRequest, error)

CreateDataTableRequest from a /polyappDataTable request URL.

type DataTableResponse

type DataTableResponse struct {
	// The draw counter that this object is a response to - from the draw parameter sent as part of the data request.
	// Note that it is strongly recommended for security reasons that you cast this parameter to an integer, rather than
	// simply echoing back to the client what it sent in the draw parameter, in order to prevent Cross Site Scripting
	// (XSS) attacks.
	Draw int `json:"draw"`
	// Total records, before filtering (i.e. the total number of records in the database)
	RecordsTotal int `json:"recordsTotal"`
	// Total records, after filtering (i.e. the total number of records after filtering has been applied - not just the
	// number of records being returned for this page of data).
	RecordsFiltered int `json:"recordsFiltered"`
	// The data to be displayed in the table. This is an array of data source objects, one for each row, which will be
	// used by DataTables. Note that this parameter's name can be changed using the ajax option's dataSrc property.
	Data [][]string `json:"data"`
	// Optional: If an error occurs during the running of the server-side processing script, you can inform the user of
	// this error by passing back the error message to be displayed using this parameter. Do not include if there is
	// no error.
	Error string `json:"error"`
	// LastDocumentID is the last returned DocumentID
	LastDocumentID string `json:"lastDocumentID"`
}

DataTableResponse is based on https://datatables.net/manual/server-side

type FieldSecurityOptions

type FieldSecurityOptions struct {
	Readonly bool
}

FieldSecurityOptions holds access control information about a particular field like: industry_domain_schema_field name

type FullTextSearchRequest

type FullTextSearchRequest struct {
	Collection string
	Fields     []string
	Value      string
}

func CreateFullTextSearchRequest

func CreateFullTextSearchRequest(URL *url.URL) (FullTextSearchRequest, error)

CreateFullTextSearchRequest from a /polyappFullTextSearch request.

type FullTextSearchResponse

type FullTextSearchResponse struct {
	Results    []FullTextSearchResponseResults
	ResultHTML string // ResultHTML is HTML which can be used to display search results in lieu of parsing the results yourself.
}

type FullTextSearchResponseResults

type FullTextSearchResponseResults struct {
	Name          string // Name or Title of the result
	HelpText      string // HelpText of the result is only available when searching collection "task".
	ChangeDataURL string // URL of the result's Change Data page
	NewDataURL    string // URL of the result's New Data page
	EditTaskURL   string // URL of the result's Edit Task page
}

type GETRequest

type GETRequest struct {
	// IndustryID is part of the context of this request.
	IndustryID string `json:"IndustryID"`
	// OverrideIndustryID is used if the URL includes 'industry' parameter.
	OverrideIndustryID string `json:"OverrideIndustryID"`
	// DomainID is part of the context of this request.
	DomainID string `json:"DomainID"`
	// OverrideDomainID is used if the URL includes 'domain' parameter.
	OverrideDomainID string `json:"OverrideDomainID"`
	// TaskID of the Task in the database is used to tie the User, Data, Schema, and UX together into a single package
	TaskID string `json:"TaskID"`
	// OverrideTaskID is used if the URL includes 'task' parameter.
	OverrideTaskID string `json:"OverrideTaskID"`
	// UXID is the ID of the UI data in the database.
	UXID string `json:"UXID"`
	// SchemaID is the ID of the Data's schema in the database.
	SchemaID string `json:"SchemaID"`
	// DataID of the data instance in the database.
	DataID string `json:"DataID"`
	// UserID of the user making the change. All access checks and the like are performed with this user.
	// Do not confuse the UserID with the UID. The UID is from Firebase; the UserID from Polyapp's Firestore DB.
	// You could have a UID of xyz and a UserID of hyk if the User were to be a Bot authorized under a human's uid.
	UserID string `json:"UserID"`
	// UserCache is a cache of the User when the request began and it was authenticated.
	// This may not change if the User is updated during the request.
	UserCache *User
	// RoleID is the role for a particular user.
	RoleID string `json:"RoleID"`
	// ModifyID is the ID of an object in the DB you wish to modify. This is used exclusively on the server
	// and is used in tasks which are modifying objects like type UX or type Schema or if you want to refresh a report.
	ModifyID string `json:"ModifyID"`
	// AltShell if true uses lightshell.index instead of index.html to wrap the response from ux.HTML.
	AltShell string `json:"AltShell"`
	// FinishURL is where this page navigates to after this Task is done. Since the FinishURL could contain a FinishURL,
	// there could be a chain of pages you'll be going back to after visiting this one.
	//
	// Should be encoded with url.PathEscape prior to being used in a URL. Should be decoded with url.PathUnescape prior
	// to being stored into the FinishURL variable.
	FinishURL string
	// PublicPath is used for public / semipublic pages to identify the path. This is used to avoid overloading TaskID.
	PublicPath string `json:"PublicPath"`
}

GETRequest is an incoming GET request. If you make changes here you may also need to make changes in type 'POSTRequest'.

func CreateGETRequest

func CreateGETRequest(URL url.URL) (GETRequest, error)

CreateGETRequest returns a GETRequest and an error if it fails. It does NOT handle retrieving the auth header. FYI this is substantially similar to public/main.js function createPOSTBody.

func ParseRef

func ParseRef(ref string) (GETRequest, error)

ParseRef is the opposite of CreateRef. It parses a string Ref into a GETRequest data structure.

func (GETRequest) GetDomain

func (request GETRequest) GetDomain() string

GetDomain considers any overrides to the domain.

func (GETRequest) GetIndustry

func (request GETRequest) GetIndustry() string

GetIndustry considers any overrides to the industry.

func (GETRequest) GetTask

func (request GETRequest) GetTask() string

GetTask considers any overrides to the task.

func (GETRequest) Validate

func (request GETRequest) Validate() error

Validate ensures the structure of GETRequest is valid

type GETResponse

type GETResponse struct {
	// All of the HTML in the page you're responding with.
	HTML string
	// RedirectURL, if set, returns an HTTP redirect to the client.
	RedirectURL string
}

GETResponse can be used to create a reply to an incoming GET request.

func (GETResponse) Validate

func (response GETResponse) Validate() error

Validate ensures the structure of GETResponse is valid

type Iter

type Iter interface {
	Next() (Queryable, error)
	Stop()
	// Length is number of available results.
	Length() int
}

Iter lets you iterate over results from a query. It is created with a function like 'CreateDBIterator'.

The Iter interface design is based on Firestore's iterator interface design.

type ModDOM

type ModDOM struct {
	// DeleteSelector is all of the elements you're trying to remove
	DeleteSelector string
	// InsertSelector is the ID of the element we're inserting near
	InsertSelector string
	// Action is the position parameter in 'insertAdjacentHTML'. Options: beforebegin afterbegin beforeend afterend
	// Action also accepts "wrap" which calls jQuery's wrap() function.
	Action string
	// HTML is what we are putting in to the DOM. It's the text parameter in 'insertAdjacentHTML'
	HTML string
	// AddClassSelector is optional and can select more than one element. If populated, AddClasses should be populated.
	AddClassSelector string
	// AddClasses is optional. It's a list of CSS classes to add to the elements found by AddClassSelector.
	AddClasses []string
	// RemoveClassSelector is optional and can select more than one element. If populated, RemoveClasses should be populated.
	RemoveClassSelector string
	// RemoveClasses is optional. It's a list of CSS classes to remove from the elements found by RemoveClassSelector.
	RemoveClasses []string
}

ModDOM contains everything the client needs to modify the DOM with 'insertAdjacentHTML' function call.

func (ModDOM) Validate

func (mod ModDOM) Validate() error

Validate ensures the structure of ModDOM is valid

type POSTRequest

type POSTRequest struct {
	// MessageID identifies this message. This ensures 1 message gets 1 response.
	MessageID string `json:"MessageID"`
	// IndustryID is part of the context of this request.
	IndustryID string `json:"IndustryID"`
	// OverrideIndustryID is used if the URL includes 'industry' parameter.
	OverrideIndustryID string `json:"OverrideIndustryID"`
	// DomainID is part of the context of this request.
	DomainID string `json:"DomainID"`
	// OverrideDomainID is used if the URL includes 'domain' parameter.
	OverrideDomainID string `json:"OverrideDomainID"`
	// TaskID of the Task in the database is used to tie the User, Data, Schema, and UX together into a single package
	TaskID string `json:"TaskID"`
	// TaskCache is a cache of the Task when it was last read. This is not necessarily updated if the Task document is updated
	// during the request.
	TaskCache *Task
	// OverrideTaskID is used if the URL includes 'task' parameter.
	OverrideTaskID string `json:"OverrideTaskID"`
	// Data is the marshalled information from the Task. Data is in a map of Document ID -> Data in that Document.
	// Within Data in that Document, there is a map from ElementID -> data like booleans, strings, numbers, etc.
	//
	// You can create this data structure with func DataToPOSTData
	Data map[string]map[string]interface{} `json:"Data"`
	// UXID is the ID of the UI data in the database.
	UXID string `json:"UXID"`
	// UXCache is a cache of the UX when it was last read. This is not necessarily updated if the UX document is updated
	// during the request.
	UXCache *UX
	// SchemaID is the ID of the Data's schema in the database.
	SchemaID string `json:"SchemaID"`
	// SchemaCache is a cache of the Schema when it was last read. This is not necessarily updated if the Schema document is updated
	// during the request.
	SchemaCache *Schema
	// DataID of the data instance in the database.
	DataID string `json:"DataID"`
	// UserID of the user making the change. All access checks and the like are performed with this user.
	UserID string `json:"UserID"`
	// UserCache is a cache of the User when it was last read. This not necessarily updated if the User document is updated
	// during the request.
	UserCache *User
	// PublicMapOwningUserID is the ID of the User which owns (created) this URL.
	PublicMapOwningUser User
	// RoleID for a Role. This is NOT required - usually the User document's Role is considered instead.
	RoleID string `json:"RoleID"`
	// ModifyID for an object which is not
	ModifyID string `json:"ModifyID"`
	// PublicPath is used for public / semipublic pages to identify the path. This is used to avoid overloading TaskID.
	PublicPath string `json:"PublicPath"`
	// RecaptchaResponse is the response from the Recaptcha API or an empty string.
	RecaptchaResponse string `json:"RecaptchaResponse"`
	// FinishURL is where this page navigates to after this Task is done. Since the FinishURL could contain a FinishURL,
	// there could be a chain of pages you'll be going back to after visiting this one.
	FinishURL string
	// IsDone is set programatically and if true indicates that Data contained the Done boolean.
	IsDone bool
	// WantDocuments are arrays of FirestoreIDs of documents the client wants.
	WantDocuments WantDocuments `json:"WantDocuments"`
}

POSTRequest contains all information sent from the client. If you're making changes here you may also need to make changes in type 'GETRequest'.

NOTE: If you modify this structure, you MUST consider the security implications for public access. See PublicMapSitePOST().

func (POSTRequest) GetDomain

func (p POSTRequest) GetDomain() string

GetDomain considers any overrides to the domain.

func (POSTRequest) GetIndustry

func (p POSTRequest) GetIndustry() string

GetIndustry considers any overrides to the industry.

func (POSTRequest) GetTask

func (p POSTRequest) GetTask() string

GetTask considers any overrides to the task.

func (*POSTRequest) Validate

func (p *POSTRequest) Validate() error

Validate ensures the structure of POSTRequest is valid

type POSTResponse

type POSTResponse struct {
	// MessageID is a randomly generated string. It's identical to MessageID on the incoming message.
	MessageID string
	// NewURL is optional. Set this to set the address bar.
	NewURL string
	// BrowserActions is optional. Setting this initiates some Javascript which performs things which aren't doable with ModDOMs,
	// like opening a new browser tab.
	BrowserActions []BrowserAction
	// DataUpdates is a subset of the marshalled information from the Task. Data is in a map of Document ID -> Data in that Document.
	// Within Data in that Document, there is a map from ElementID -> data like booleans, strings, numbers, etc.
	//
	// You can create this data structure with func DataToPOSTData
	DataUpdates map[string]map[string]interface{}
	// DataDocuments can be cached on the client in their local database.
	DataDocuments []*Data
	// RoleDocuments can be cached on the client in their local database.
	RoleDocuments []*Role
	// SchemaDocuments can be cached on the client in their local database.
	SchemaDocuments []*Schema
	// TaskDocuments can be cached on the client in their local database.
	TaskDocuments []*Task
	// UserDocuments can be cached on the client in their local database.
	UserDocuments []*User
	// UXDocuments can be cached on the client in their local database.
	UXDocuments []*UX
	// ModDOMs are things we're putting in to the DOM.
	ModDOMs []ModDOM
}

POSTResponse applies some changes to the DOM (ModDOMs) in order.

func (*POSTResponse) Init

func (r *POSTResponse) Init(request POSTRequest)

Init initializes the common.POSTResponse structure and removes any of its current contents.

func (POSTResponse) Validate

func (r POSTResponse) Validate() error

Validate ensures the structure of POSTResponse is valid

type PublicMap

type PublicMap struct {
	FirestoreID string `json:"polyappFirestoreID"`
	// HostAndPath includes subdomains, like: "example.com" or "a.example.com", and probably looks like this: "example.com/pay/"
	// typical construction looks like: c.Request().Host + "/" + c.Request().URL.EscapedPath()
	//
	// The HostAndPath ALWAYS ends in a /
	HostAndPath string
	// IndustryID is the Industry associated with this HostAndPath.
	IndustryID string
	// DomainID is the domain associated with this HostAndPath.
	DomainID string
	// UXID is the UX associated with this HostAndPath, if any.
	UXID *string
	// TaskID is the Task associated with this HostAndPath, if any.
	TaskID *string
	// SchemaID is the Schema associated with this HostAndPath, if any.
	SchemaID *string
	// OwningUserID is a FirestoreID for whatever user owns this PublicMap. This is set when the PublicMap is created
	// and is used to verify that this page is being paid for (aka the subscription is still active).
	OwningUserID string
	Deprecated   *bool `json:"polyappDeprecated"`
}

PublicMap is used as a lookup mechanism when trying to find the content of a web page. For instance, if you wanted to find the content of "example.com/pay" you would first query for that URL in the HostAndPath parameter. You would then take the first (and only) result and examine its UXID, TaskID, and other data and use that to construct a request in the form of private requests. You can then use that private request to access the rest of the code normally.

func (*PublicMap) CollectionName

func (p *PublicMap) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*PublicMap) GetFirestoreID

func (p *PublicMap) GetFirestoreID() string

GetFirestoreID returns the FirestoreID property.

func (*PublicMap) Init

func (p *PublicMap) Init(simple map[string]interface{}) error

Init the PublicMap with the simple map.

func (*PublicMap) Simplify

func (p *PublicMap) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*PublicMap) Validate

func (p *PublicMap) Validate() error

Validate returns an error for improper data.

type PublicSiteTemplate

type PublicSiteTemplate struct {
	IsPublic        bool
	AllContent      string
	TopBar          string
	LeftBar         string
	RightBar        string
	BottomBar       string
	Title           string
	MetaDescription string
	MetaAuthor      string
	MetaKeywords    string
	HeadTag         string // same as HeadTag in UX.
	IconPath        string
	CSSPath         string
	FullAuthPage    bool // True and this will be treated as a page requiring full Auth.
}

PublicSiteTemplate includes the inputs to the template at genPublic/index.html

type Query

type Query interface {
	// Init the query. This should be called immediately after creating the Query object.
	Init(industryID string, domainID string, schemaID string, firestoreCollection string)
	// AddEquals ensures the key k's value == v, a string
	AddEquals(k string, v string)
	// AddArrayContains ensures the key k's value, which should be an array, contains the value v.
	AddArrayContains(k string, v string) error
	// QueryRead uses the query to perform a read via an Iterator interface.
	QueryRead() (Iter, error)
}

Query in a database-agnostic way. You must Init the Query, optionally add query constraints like "AddEquals", and then call QueryRead(). QueryRead() returns an iterator interface which can be used to iterate over the query results.

The Query interface design is based on Firestore's query interface design.

type Queryable

type Queryable interface {
	Validate() error
	Simplify() (map[string]interface{}, error)
	GetFirestoreID() string
	// Init should populate the structure with the contents of 'simple'. It assumes there are no existing contents.
	//
	// Init implementations MUST set Queryable.FirestoreID = simple[polyappFirestoreID].
	Init(simple map[string]interface{}) error
	// CollectionName is something like common.CollectionData or "schema". This helps with the creation of generic methods.
	CollectionName() string
}

Queryable implementation is a prerequisite for using the Query interface because Query needs to use Iter which returns a Queryable.

type Role

type Role struct {
	FirestoreID string `json:"polyappFirestoreID" suffix:"Role ID"`
	Name        string // Name of this role. Used when searching for roles.
	// Access defines to what a Role grants access.
	//
	// key: industry_domain
	// value: one or more of the letters 'c' 'r' 'u' 'd'
	//
	// "domain" could contain an underscore. But since no Industry contains an underscore we know the first underscore must be the delimiter.
	Access     []Access `json:"Access" suffix:"Access"`
	Deprecated *bool    `json:"polyappDeprecated" firestore:"polyappDeprecated"`
}

Role is a Role-collection object pointed to by a User. Its arrays can be nil; that just means they weren't set in the request.

Note: Access is stored as a map in the database. Firestore supports []map[string]interface{}

func (*Role) CollectionName

func (r *Role) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Role) GetFirestoreID

func (r *Role) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*Role) Init

func (r *Role) Init(simple map[string]interface{}) error

Init translates a raw map into the Role type. It does NOT set FirestoreID.

func (*Role) Simplify

func (r *Role) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*Role) Validate

func (r *Role) Validate() error

Validate returns an error for improper data.

type Schema

type Schema struct {
	FirestoreID  string  `json:"polyappFirestoreID"` // For this document
	NextSchemaID *string // linked list pointing to the next schema version's ID in the database.
	Name         *string // Used when searching the collection. Typically identical to Task's Name.
	IndustryID   string  `json:"polyappIndustryID" firestore:"polyappIndustryID"` // FirestoreID
	DomainID     string  `json:"polyappDomainID" firestore:"polyappDomainID"`     // FirestoreID
	SchemaID     string  `json:"polyappSchemaID" firestore:"polyappSchemaID"`     // FirestoreID
	// Field Name -> Type. "Ref" "S" "I" "B" "F" are the non-array options. There's also "ARef", "AS", "AI", "AB", "AF". There's also "Bytes" for Bytes.
	DataTypes    map[string]string
	DataHelpText map[string]string // Field Name -> Help Text.
	DataKeys     []string          // List of Keys (Field Names) which are used in DataTypes. Used to find duplicate keys.
	// IsPublic is true if this is publicly accessible. It is false or not set otherwise.
	IsPublic   bool
	Deprecated *bool `json:"polyappDeprecated" firestore:"polyappDeprecated"`
}

Schema of some Data. Schema must play well with both the corresponding Data and the UX and Task.

func SchemaConvertSchema

func SchemaConvertSchema(oldSchema *Schema, newFirestoreID string) (Schema, error)

SchemaConvertSchema converts a Schema into a new Schema. For example, convert schema with ID "A" to one with ID "B".

The new Schema is returned.

func (*Schema) CollectionName

func (s *Schema) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Schema) GetFirestoreID

func (s *Schema) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*Schema) Init

func (s *Schema) Init(simple map[string]interface{}) error

Init translates a raw map into the Schema type. It does NOT set FirestoreID.

func (*Schema) Merge

func (s *Schema) Merge(toMerge *Schema) error

Merge merges a Schema 'toMerge' into the pointer receiver. It requires that they have the same FirestoreID.

func (*Schema) Simplify

func (s *Schema) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*Schema) Validate

func (s *Schema) Validate() error

Validate return nil if the structure is valid.

type SelectSearchRequest

type SelectSearchRequest struct {
	Collection string
	Field      string
	Value      string
}

func CreateSelectSearchRequest

func CreateSelectSearchRequest(URL *url.URL) (SelectSearchRequest, error)

CreateSelectSearchRequest from a /polyappSearch request.

type SelectSearchResponse

type SelectSearchResponse struct {
	ResultHTML string
}

type Task

type Task struct {
	FirestoreID string `json:"polyappFirestoreID"`
	IndustryID  string `json:"polyappIndustryID"`
	DomainID    string `json:"polyappDomainID"`
	// Name associated with the Task.
	Name string
	// HelpText associated with the Task.
	HelpText string
	// TaskGoals should always be created programmatically with TaskGoalAdd()
	//
	// TaskGoals should always be evaluated with TaskGoalEvaluate()
	//
	// TaskGoals are stored internally in a JSON format derived from the 'taskGoal' struct with keys which are either
	// prefixed with 'polyappError' which means they're field-specific or which are NOT prefixed & therefore task-level.
	TaskGoals map[string]string
	// FieldSecurity holds access control properties which are related to the ability to access a field.
	FieldSecurity map[string]*FieldSecurityOptions
	// BotsTriggeredAtLoad are IDs of Bots which are triggered on every GET request for this task.
	// When creating one of these Bots, keep in mind that the user will be waiting at a loading screen while this is happening.
	BotsTriggeredAtLoad []string
	// BotsTriggeredContinuously are IDs of Bots which are triggered on every POST request.
	// Since POST requests are triggered automatically and periodically as part of auto-save these Bots run frequently.
	//
	// These Bots run before BotsTriggeredAtDone.
	BotsTriggeredContinuously []string
	// BotsTriggeredAtDone are IDs of Bots which are triggered when the "Done" value is set to True.
	BotsTriggeredAtDone []string
	// BotStaticData is provided to bots when they are run. This data is supposed to be read-only and encapsulates
	// information which changes on a Task by Task basis but not a Data by Data basis. For example there is an Action
	// which can open a new Data for a Task when a user clicks "Done". This Action allows you to configure which Data to
	// open next by inputting a Query into the Task's BotStaticData. The Action knows which current Data
	// you are on, so it can run the Query with StartAfter set to the current data, thereby returning the next result
	// in the Query.
	//
	// Keys are the last chunk of fields after the last "_" (aka the field's Name). Values are arbitrary and might be parsed to ints or floats.
	BotStaticData map[string]string
	// IsPublic is true if this is publicly accessible. It is false or not set otherwise.
	//
	// IsPublic may eventually be deprecated when we figure out a more granular access control method.
	IsPublic   bool
	Deprecated *bool `json:"polyappDeprecated" firestore:"polyappDeprecated"`
}

Task stores validation requirements, a list of bots, and bot configuration data. Its Name is shown at the top of the form which you see when loading the Task.

When someone thinks of Polyapp they will probably think of Tasks as a collection of things they see, the database schema, and the validation for the Task. In reality the Task struct and document in the database has no reference to the schema or the user interface or the data.

The Task is not directly coupled to Data, Schema, or UX. Instead it can merely be 'satisfied' by the Data. This setup means one Task could be used with multiple Schema, UX, or Data.

func SchemaConvertTask

func SchemaConvertTask(task *Task, newSchemaID string) (Task, error)

func (*Task) CollectionName

func (t *Task) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*Task) GetFirestoreID

func (t *Task) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*Task) Init

func (t *Task) Init(simple map[string]interface{}) error

Init translates a raw map into the Task type. It does NOT set FirestoreID.

func (*Task) Simplify

func (t *Task) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*Task) TaskGoalAdd

func (t *Task) TaskGoalAdd(associatedField string, name string, helpText string, op1Variable string, operation string, op2Variable string, op2Constant interface{}, errorMessage string) error

TaskGoalAdd adds a Task Goal to the TaskGoals map. Include op2Variable XOR op2Constant.

Inputs:

associatedField: the name of a field an error should be shown underneath. Typically associatedField == op1Variable || associatedField == op2Variable. If associatedField is not set, key = name. If set, key = "polyappError" + name

name: the user-readable (and human-searchable) name of this validator.

helpText: a user-readable description of why this validator exists, etc.

op1Variable: if [op1Variable] [operation] [op2Variable] { return "" } else { return errorMessage }

operation: if [op1Variable] [operation] [op2Variable] { return "" } else { return errorMessage }

op2Variable: if [op1Variable] [operation] [op2Variable] { return "" } else { return errorMessage }

op2Constant: if [op1Variable] [operation] [op2Constant] { return "" } else { return errorMessage }

errorMessage: if [op1Variable] [operation] [op2Variable] { return "" } else { return errorMessage }

func (*Task) Validate

func (t *Task) Validate() error

Validate returns an error for improper data.

type UX

type UX struct {
	FirestoreID string `json:"polyappFirestoreID"`
	IndustryID  string `json:"polyappIndustryID"`
	DomainID    string `json:"polyappDomainID"`
	SchemaID    string `json:"polyappSchemaID"`
	// HTML is everything this UX needs to host some Task or Tasks. It does NOT include navigation or metadata.
	HTML *string
	// Navbar is the navigation bar HTML.
	Navbar *string
	// Title is a value in the header of an HTML page: https://www.w3schools.com/tags/tag_title.asp
	Title *string
	// MetaDescription is one of the named meta HTML elements: https://www.w3schools.com/tags/tag_meta.asp
	MetaDescription *string
	// MetaKeywords is one of the named meta HTML elements: https://www.w3schools.com/tags/tag_meta.asp
	MetaKeywords *string
	// MetaAuthor is one of the named meta HTML elements: https://www.w3schools.com/tags/tag_meta.asp
	MetaAuthor *string
	// HeadTag contains additional content which is directly placed into the Head tag. This could include scripts.
	HeadTag *string
	// IconPath is the path to an icon you can show in the upper right hand corner.
	IconPath *string
	// CSSPath is the path you can call to get a custom CSS file. If not set, the CSS which is loaded is Bootstrap's default CSS.
	//
	// This may also be known as "ThemePath" from the User.
	CSSPath *string
	// SelectFields are Fields / UI IDs in THIS SCHEMA -> search field, aka the Field in the searched schema.
	// To use this, when the UI is being loaded you must run the query given in the value field. Use the values you
	// get from that query to populate the Select markup options.
	//
	// This could also be implemented via a data-* attribute, but doing so would require trusting the client to decide
	// what to search. Since searching is inherently more expensive than other operations things have been implemented
	// this way instead.
	SelectFields map[string]string
	// IsPublic is true if this is publicly accessible. It is false or not set otherwise.
	IsPublic bool
	// FullAuthPage is True if you want to demand users use full authentication.
	FullAuthPage *bool
	Deprecated   *bool `json:"polyappDeprecated" firestore:"polyappDeprecated"`
}

UX contains both the UI as stored in the HTML field and data which helps render that UI.

This structure is at the bottom of a dependency chain. Task is satisfied by some Schemas; Schema is satisfied by Data which is following that Schema. UX is different in that it can contain nested components which could come from different Data documents which follow different Schemas. So a simple UX with no ARef or Ref is satisfied by a single Schema, but if it contains ARef or Ref keys the schema must contain those too.

func SchemaConvertUX

func SchemaConvertUX(oldUX *UX, newSchemaID string) (UX, error)

func (*UX) CollectionName

func (ux *UX) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*UX) GetFirestoreID

func (ux *UX) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*UX) Init

func (ux *UX) Init(simple map[string]interface{}) error

Init translates a raw map into the UX type. It does NOT set FirestoreID.

func (*UX) Simplify

func (ux *UX) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*UX) Validate

func (ux *UX) Validate() error

Validate returns an error for improper data.

type User

type User struct {
	FirestoreID   string  `json:"polyappFirestoreID" suffix:"User ID"`
	UID           *string `suffix:"UID"`
	FullName      *string `suffix:"Full Name"`
	PhotoURL      *string `suffix:"Photo URL"`
	EmailVerified *bool   `suffix:"Email Verified"`
	PhoneNumber   *string `suffix:"Phone Number"`
	Email         *string `suffix:"Email"`
	// Roles holds Firestore IDs of Role documents
	Roles []string `suffix:"Roles"`
	// BotsTriggeredAtLoad are triggered every time this User loads a Task.
	BotsTriggeredAtLoad []string `suffix:"Bots Triggered At Load"`
	// BotsTriggeredAtDone are triggered every time this User clicks "Done" on a Task.
	BotsTriggeredAtDone []string `suffix:"Bots Triggered At Done"`
	HomeTask            string   `suffix:"Home Task"`
	HomeUX              string   `suffix:"Home UX"`
	HomeSchema          string   `suffix:"Home Schema"`
	HomeData            string   `suffix:"Home Data"`
	// ThemePath like /blob/assets/??/polyapp_TaskSchemaUX_KxZRoxrRpyECTbCudVfXKnmHB_Bootstrap%20Theme?cacheBuster=1628015252293
	ThemePath  *string `suffix:"Theme Path"`
	Deprecated *bool   `json:"polyappDeprecated" firestore:"polyappDeprecated"`
}

User is someone using the software. Roles can be nil.

func (*User) CollectionName

func (u *User) CollectionName() string

CollectionName is the literal name of this collection. This is used by generic functions.

func (*User) GetFirestoreID

func (u *User) GetFirestoreID() string

GetFirestoreID returns this firestore ID.

func (*User) Init

func (u *User) Init(simple map[string]interface{}) error

Init translates a raw map into the User type. It sets FirestoreID if possible.

func (*User) Simplify

func (u *User) Simplify() (map[string]interface{}, error)

Simplify into a map[string]interface{}. This is the opposite of Init().

func (*User) Validate

func (u *User) Validate() error

Validate returns an error for improper data.

type WantDocuments

type WantDocuments struct {
	Data   []string
	Role   []string
	Schema []string
	Task   []string
	User   []string
	UX     []string
}

WantDocuments are arrays of FirestoreIDs of documents the client wants.

Jump to

Keyboard shortcuts

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