github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/pkg/database/plugin.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package database
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/kaleido-io/firefly/internal/config"
    23  	"github.com/kaleido-io/firefly/internal/i18n"
    24  	"github.com/kaleido-io/firefly/pkg/fftypes"
    25  )
    26  
    27  var (
    28  	// HashMismatch sentinel error
    29  	HashMismatch = i18n.NewError(context.Background(), i18n.MsgHashMismatch)
    30  	// IDMismatch sentinel error
    31  	IDMismatch = i18n.NewError(context.Background(), i18n.MsgIDMismatch)
    32  	// DeleteRecordNotFound sentinel error
    33  	DeleteRecordNotFound = i18n.NewError(context.Background(), i18n.Msg404NotFound)
    34  )
    35  
    36  // Plugin is the interface implemented by each plugin
    37  type Plugin interface {
    38  	PeristenceInterface // Split out to aid pluggability the next level down (SQL provider etc.)
    39  
    40  	// InitPrefix initializes the set of configuration options that are valid, with defaults. Called on all plugins.
    41  	InitPrefix(prefix config.Prefix)
    42  
    43  	// Init initializes the plugin, with configuration
    44  	// Returns the supported featureset of the interface
    45  	Init(ctx context.Context, prefix config.Prefix, callbacks Callbacks) error
    46  
    47  	// Capabilities returns capabilities - not called until after Init
    48  	Capabilities() *Capabilities
    49  }
    50  
    51  // PeristenceInterface are the operations that must be implemented by a database interfavce plugin.
    52  // The database mechanism of Firefly is designed to provide the balance between being able
    53  // to query the data a member of the network has transferred/received via Firefly efficiently,
    54  // while not trying to become the core database of the application (where full deeply nested
    55  // rich query is needed).
    56  //
    57  // This means that we treat business data as opaque within the stroage, only verifying it against
    58  // a data definition within the Firefly core runtime itself.
    59  // The data types, indexes and relationships are designed to be simple, and map closely to the
    60  // REST semantics of the Firefly API itself.
    61  //
    62  // As a result, the database interface could be implemented efficiently by most database technologies.
    63  // Including both Relational/SQL and Document/NoSQL database technologies.
    64  //
    65  // As such we suggest the factors in choosing your database should be non-functional, such as:
    66  // - Which provides you with the HA/DR capabilities you require
    67  // - Which is most familiar within your existing devops pipeline for the application
    68  // - Whether you can consolidate the HA/DR and server infrastructure for your app DB with the Firefly DB
    69  //
    70  // Each database does need an update to the core codebase, to provide a plugin that implements this
    71  // interface.
    72  // For SQL databases the process of adding a new database is simplified via the common SQL layer.
    73  // For NoSQL databases, the code should be straight forward to map the collections, indexes, and operations.
    74  //
    75  type PeristenceInterface interface {
    76  	fftypes.Named
    77  
    78  	// RunAsGroup instructs the database plugin that all database operations performed within the context
    79  	// function can be grouped into a single transaction (if supported).
    80  	// Note, the caller is responsible for passing the context back to all database operations performed within the supplied function.
    81  	RunAsGroup(ctx context.Context, fn func(ctx context.Context) error) error
    82  
    83  	// UpsertNamespace - Upsert a namespace
    84  	// Throws IDMismatch error if updating and ids don't match
    85  	UpsertNamespace(ctx context.Context, data *fftypes.Namespace, allowExisting bool) (err error)
    86  
    87  	// UpdateNamespace - Update namespace
    88  	UpdateNamespace(ctx context.Context, id *fftypes.UUID, update Update) (err error)
    89  
    90  	// DeleteNamespace - Delete namespace
    91  	DeleteNamespace(ctx context.Context, id *fftypes.UUID) (err error)
    92  
    93  	// GetNamespace - Get an namespace by name
    94  	GetNamespace(ctx context.Context, name string) (offset *fftypes.Namespace, err error)
    95  
    96  	// GetNamespaces - Get namespaces
    97  	GetNamespaces(ctx context.Context, filter Filter) (offset []*fftypes.Namespace, err error)
    98  
    99  	// UpsertMessage - Upsert a message, with all the embedded data references.
   100  	// allowHashUpdate=false throws HashMismatch error if the updated message has a different hash
   101  	UpsertMessage(ctx context.Context, message *fftypes.Message, allowExisting, allowHashUpdate bool) (err error)
   102  
   103  	// InsertMessageLocal - sets a boolean flag on inserting a new message (cannot be an update) to state it is local.
   104  	// Only time this flag is ever set. Subsequent updates can affect other fields, but not the local flag. Important to stop infinite message propagation.
   105  	InsertMessageLocal(ctx context.Context, message *fftypes.Message) (err error)
   106  
   107  	// UpdateMessage - Update message
   108  	UpdateMessage(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   109  
   110  	// UpdateMessages - Update messages
   111  	UpdateMessages(ctx context.Context, filter Filter, update Update) (err error)
   112  
   113  	// GetMessageByID - Get a message by ID
   114  	GetMessageByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Message, err error)
   115  
   116  	// GetMessages - List messages, reverse sorted (newest first) by Confirmed then Created, with pagination, and simple must filters
   117  	GetMessages(ctx context.Context, filter Filter) (message []*fftypes.Message, err error)
   118  
   119  	// GetMessageRefs - Lighter weight query to just get the reference info of messages
   120  	GetMessageRefs(ctx context.Context, filter Filter) ([]*fftypes.MessageRef, error)
   121  
   122  	// GetMessagesForData - List messages where there is a data reference to the specified ID
   123  	GetMessagesForData(ctx context.Context, dataID *fftypes.UUID, filter Filter) (message []*fftypes.Message, err error)
   124  
   125  	// UpsertData - Upsert a data record
   126  	// allowHashUpdate=false throws HashMismatch error if the updated message has a different hash
   127  	UpsertData(ctx context.Context, data *fftypes.Data, allowExisting, allowHashUpdate bool) (err error)
   128  
   129  	// UpdateData - Update data
   130  	UpdateData(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   131  
   132  	// GetDataByID - Get a data record by ID
   133  	GetDataByID(ctx context.Context, id *fftypes.UUID, withValue bool) (message *fftypes.Data, err error)
   134  
   135  	// GetData - Get data
   136  	GetData(ctx context.Context, filter Filter) (message []*fftypes.Data, err error)
   137  
   138  	// GetDataRefs - Get data references only (no data)
   139  	GetDataRefs(ctx context.Context, filter Filter) (message fftypes.DataRefs, err error)
   140  
   141  	// UpsertBatch - Upsert a batch
   142  	// allowHashUpdate=false throws HashMismatch error if the updated message has a different hash
   143  	UpsertBatch(ctx context.Context, data *fftypes.Batch, allowExisting, allowHashUpdate bool) (err error)
   144  
   145  	// UpdateBatch - Update data
   146  	UpdateBatch(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   147  
   148  	// GetBatchByID - Get a batch by ID
   149  	GetBatchByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Batch, err error)
   150  
   151  	// GetBatches - Get batches
   152  	GetBatches(ctx context.Context, filter Filter) (message []*fftypes.Batch, err error)
   153  
   154  	// UpsertTransaction - Upsert a transaction
   155  	// allowHashUpdate=false throws HashMismatch error if the updated message has a different hash
   156  	UpsertTransaction(ctx context.Context, data *fftypes.Transaction, allowExisting, allowHashUpdate bool) (err error)
   157  
   158  	// UpdateTransaction - Update transaction
   159  	UpdateTransaction(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   160  
   161  	// GetTransactionByID - Get a transaction by ID
   162  	GetTransactionByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Transaction, err error)
   163  
   164  	// GetTransactions - Get transactions
   165  	GetTransactions(ctx context.Context, filter Filter) (message []*fftypes.Transaction, err error)
   166  
   167  	// UpsertDatatype - Upsert a data definitino
   168  	UpsertDatatype(ctx context.Context, datadef *fftypes.Datatype, allowExisting bool) (err error)
   169  
   170  	// UpdateDatatype - Update data definition
   171  	UpdateDatatype(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   172  
   173  	// GetDatatypeByID - Get a data definition by ID
   174  	GetDatatypeByID(ctx context.Context, id *fftypes.UUID) (datadef *fftypes.Datatype, err error)
   175  
   176  	// GetDatatypeByName - Get a data definition by name
   177  	GetDatatypeByName(ctx context.Context, ns, name, version string) (datadef *fftypes.Datatype, err error)
   178  
   179  	// GetDatatypes - Get data definitions
   180  	GetDatatypes(ctx context.Context, filter Filter) (datadef []*fftypes.Datatype, err error)
   181  
   182  	// UpsertOffset - Upsert an offset
   183  	UpsertOffset(ctx context.Context, data *fftypes.Offset, allowExisting bool) (err error)
   184  
   185  	// UpdateOffset - Update offset
   186  	UpdateOffset(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   187  
   188  	// GetOffset - Get an offset by name
   189  	GetOffset(ctx context.Context, t fftypes.OffsetType, ns, name string) (offset *fftypes.Offset, err error)
   190  
   191  	// GetOffsets - Get offsets
   192  	GetOffsets(ctx context.Context, filter Filter) (offset []*fftypes.Offset, err error)
   193  
   194  	// DeleteOffset - Delete an offset by name
   195  	DeleteOffset(ctx context.Context, t fftypes.OffsetType, ns, name string) (err error)
   196  
   197  	// UpsertPin - Will insert a pin at the end of the sequence, unless the batch+hash+index sequence already exists
   198  	UpsertPin(ctx context.Context, parked *fftypes.Pin) (err error)
   199  
   200  	// GetPins - Get pins
   201  	GetPins(ctx context.Context, filter Filter) (offset []*fftypes.Pin, err error)
   202  
   203  	// SetPinDispatched - Set the dispatched flag to true on the specified pins
   204  	SetPinDispatched(ctx context.Context, sequence int64) (err error)
   205  
   206  	// DeletePin - Delete a pin
   207  	DeletePin(ctx context.Context, sequence int64) (err error)
   208  
   209  	// UpsertOperation - Upsert an operation
   210  	UpsertOperation(ctx context.Context, operation *fftypes.Operation, allowExisting bool) (err error)
   211  
   212  	// UpdateOperation - Update operation by ID
   213  	UpdateOperation(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   214  
   215  	// GetOperationByID - Get an operation by ID
   216  	GetOperationByID(ctx context.Context, id *fftypes.UUID) (operation *fftypes.Operation, err error)
   217  
   218  	// GetOperations - Get operation
   219  	GetOperations(ctx context.Context, filter Filter) (operation []*fftypes.Operation, err error)
   220  
   221  	// UpsertSubscription - Upsert a subscription
   222  	UpsertSubscription(ctx context.Context, data *fftypes.Subscription, allowExisting bool) (err error)
   223  
   224  	// UpdateSubscription - Update subscription
   225  	// Throws IDMismatch error if updating and ids don't match
   226  	UpdateSubscription(ctx context.Context, ns, name string, update Update) (err error)
   227  
   228  	// GetSubscriptionByName - Get an subscription by name
   229  	GetSubscriptionByName(ctx context.Context, ns, name string) (offset *fftypes.Subscription, err error)
   230  
   231  	// GetSubscriptionByID - Get an subscription by id
   232  	GetSubscriptionByID(ctx context.Context, id *fftypes.UUID) (offset *fftypes.Subscription, err error)
   233  
   234  	// GetSubscriptions - Get subscriptions
   235  	GetSubscriptions(ctx context.Context, filter Filter) (offset []*fftypes.Subscription, err error)
   236  
   237  	// DeleteSubscriptionByID - Delete a subscription
   238  	DeleteSubscriptionByID(ctx context.Context, id *fftypes.UUID) (err error)
   239  
   240  	// UpsertEvent - Upsert an event
   241  	UpsertEvent(ctx context.Context, data *fftypes.Event, allowExisting bool) (err error)
   242  
   243  	// UpdateEvent - Update event
   244  	UpdateEvent(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   245  
   246  	// GetEventByID - Get a event by ID
   247  	GetEventByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Event, err error)
   248  
   249  	// GetEvents - Get events
   250  	GetEvents(ctx context.Context, filter Filter) (message []*fftypes.Event, err error)
   251  
   252  	// UpsertOrganization - Upsert an organization
   253  	UpsertOrganization(ctx context.Context, data *fftypes.Organization, allowExisting bool) (err error)
   254  
   255  	// UpdateOrganization - Update organization
   256  	UpdateOrganization(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   257  
   258  	// GetOrganizationByIdentity - Get a organization by identity
   259  	GetOrganizationByIdentity(ctx context.Context, identity string) (org *fftypes.Organization, err error)
   260  
   261  	// GetOrganizationByName - Get a organization by name
   262  	GetOrganizationByName(ctx context.Context, name string) (org *fftypes.Organization, err error)
   263  
   264  	// GetOrganizationByID - Get a organization by ID
   265  	GetOrganizationByID(ctx context.Context, id *fftypes.UUID) (org *fftypes.Organization, err error)
   266  
   267  	// GetOrganizations - Get organizations
   268  	GetOrganizations(ctx context.Context, filter Filter) (org []*fftypes.Organization, err error)
   269  
   270  	// UpsertNode - Upsert a node
   271  	UpsertNode(ctx context.Context, data *fftypes.Node, allowExisting bool) (err error)
   272  
   273  	// UpdateNode - Update node
   274  	UpdateNode(ctx context.Context, id *fftypes.UUID, update Update) (err error)
   275  
   276  	// GetNode - Get a node by ID
   277  	GetNode(ctx context.Context, owner, name string) (node *fftypes.Node, err error)
   278  
   279  	// GetNodeByID- Get a node by ID
   280  	GetNodeByID(ctx context.Context, id *fftypes.UUID) (node *fftypes.Node, err error)
   281  
   282  	// GetNodes - Get nodes
   283  	GetNodes(ctx context.Context, filter Filter) (node []*fftypes.Node, err error)
   284  
   285  	// UpserGroup - Upsert a group
   286  	UpsertGroup(ctx context.Context, data *fftypes.Group, allowExisting bool) (err error)
   287  
   288  	// UpdateGroup - Update group
   289  	UpdateGroup(ctx context.Context, hash *fftypes.Bytes32, update Update) (err error)
   290  
   291  	// GetGroupByHash - Get a group by ID
   292  	GetGroupByHash(ctx context.Context, hash *fftypes.Bytes32) (node *fftypes.Group, err error)
   293  
   294  	// GetGroups - Get groups
   295  	GetGroups(ctx context.Context, filter Filter) (node []*fftypes.Group, err error)
   296  
   297  	// UpsertNonceNext - Upsert a context, assigning zero if not found, or the next nonce if it is
   298  	UpsertNonceNext(ctx context.Context, context *fftypes.Nonce) (err error)
   299  
   300  	// GetNonce - Get a context by hash
   301  	GetNonce(ctx context.Context, hash *fftypes.Bytes32) (message *fftypes.Nonce, err error)
   302  
   303  	// GetNonces - Get contexts
   304  	GetNonces(ctx context.Context, filter Filter) (node []*fftypes.Nonce, err error)
   305  
   306  	// DeleteNonce - Delete context by hash
   307  	DeleteNonce(ctx context.Context, hash *fftypes.Bytes32) (err error)
   308  
   309  	// InsertNextPin - insert a nextpin
   310  	InsertNextPin(ctx context.Context, nextpin *fftypes.NextPin) (err error)
   311  
   312  	// GetNextPinByContextAndIdentity - lookup nextpin by context+identity
   313  	GetNextPinByContextAndIdentity(ctx context.Context, context *fftypes.Bytes32, identity string) (message *fftypes.NextPin, err error)
   314  
   315  	// GetNextPinByHash - lookup nextpin by its hash
   316  	GetNextPinByHash(ctx context.Context, hash *fftypes.Bytes32) (message *fftypes.NextPin, err error)
   317  
   318  	// GetNextPins - get nextpins
   319  	GetNextPins(ctx context.Context, filter Filter) (message []*fftypes.NextPin, err error)
   320  
   321  	// UpdateNextPin - update a next hash using its local database ID
   322  	UpdateNextPin(ctx context.Context, sequence int64, update Update) (err error)
   323  
   324  	// DeleteNextPin - delete a next hash, using its local database ID
   325  	DeleteNextPin(ctx context.Context, sequence int64) (err error)
   326  }
   327  
   328  // Callbacks are the methods for passing data from plugin to core
   329  //
   330  // If Capabilities returns ClusterEvents=true then these should be brodcast to every instance within
   331  // a cluster that is connected to the database.
   332  //
   333  // If Capabilities returns ClusterEvents=false then these events can be simply coupled in-process to
   334  // update activities.
   335  //
   336  // The system does not rely on these events exclusively for data/transaction integrity, but if an event is
   337  // missed/delayed it might result in slower processing.
   338  // For example, the batch interface will initiate a batch as soon as an event is triggered, but it will use
   339  // a subsequent database query as the source of truth of the latest set/order of data, and it will periodically
   340  // check for new messages even if it does not receive any events.
   341  //
   342  // TODO: Clarify the relationship between Leader Election capabilities and Event capabilities
   343  //
   344  type Callbacks interface {
   345  	MessageCreated(sequence int64)
   346  	PinCreated(sequence int64)
   347  	EventCreated(sequence int64)
   348  	SubscriptionCreated(id *fftypes.UUID)
   349  	SubscriptionDeleted(id *fftypes.UUID)
   350  }
   351  
   352  // Capabilities defines the capabilities a plugin can report as implementing or not
   353  type Capabilities struct {
   354  	ClusterEvents bool
   355  }
   356  
   357  // NamespaceQueryFactory filter fields for namespaces
   358  var NamespaceQueryFactory = &queryFields{
   359  	"id":          &UUIDField{},
   360  	"message":     &UUIDField{},
   361  	"type":        &StringField{},
   362  	"name":        &StringField{},
   363  	"description": &StringField{},
   364  	"created":     &TimeField{},
   365  	"confirmed":   &TimeField{},
   366  }
   367  
   368  // MessageQueryFactory filter fields for messages
   369  var MessageQueryFactory = &queryFields{
   370  	"id":        &UUIDField{},
   371  	"cid":       &UUIDField{},
   372  	"namespace": &StringField{},
   373  	"type":      &StringField{},
   374  	"author":    &StringField{},
   375  	"topics":    &FFNameArrayField{},
   376  	"tag":       &StringField{},
   377  	"group":     &StringField{},
   378  	"created":   &TimeField{},
   379  	"hash":      &StringField{},
   380  	"pins":      &StringField{},
   381  	"confirmed": &TimeField{},
   382  	"sequence":  &Int64Field{},
   383  	"tx.type":   &StringField{},
   384  	"batch":     &UUIDField{},
   385  	"local":     &BoolField{},
   386  }
   387  
   388  // BatchQueryFactory filter fields for batches
   389  var BatchQueryFactory = &queryFields{
   390  	"id":         &UUIDField{},
   391  	"namespace":  &StringField{},
   392  	"type":       &StringField{},
   393  	"author":     &StringField{},
   394  	"group":      &StringField{},
   395  	"hash":       &StringField{},
   396  	"payloadref": &StringField{},
   397  	"created":    &TimeField{},
   398  	"confirmed":  &TimeField{},
   399  	"tx.type":    &StringField{},
   400  	"tx.id":      &UUIDField{},
   401  }
   402  
   403  // TransactionQueryFactory filter fields for transactions
   404  var TransactionQueryFactory = &queryFields{
   405  	"id":         &UUIDField{},
   406  	"type":       &StringField{},
   407  	"signer":     &StringField{},
   408  	"status":     &StringField{},
   409  	"reference":  &UUIDField{},
   410  	"protocolid": &StringField{},
   411  	"created":    &TimeField{},
   412  	"sequence":   &Int64Field{},
   413  	"info":       &JSONField{},
   414  	"namespace":  &StringField{},
   415  }
   416  
   417  // DataQueryFactory filter fields for data
   418  var DataQueryFactory = &queryFields{
   419  	"id":               &UUIDField{},
   420  	"namespace":        &StringField{},
   421  	"validator":        &StringField{},
   422  	"datatype.name":    &StringField{},
   423  	"datatype.version": &StringField{},
   424  	"hash":             &StringField{},
   425  	"created":          &TimeField{},
   426  }
   427  
   428  // DatatypeQueryFactory filter fields for data definitions
   429  var DatatypeQueryFactory = &queryFields{
   430  	"id":        &UUIDField{},
   431  	"message":   &UUIDField{},
   432  	"namespace": &StringField{},
   433  	"validator": &StringField{},
   434  	"name":      &StringField{},
   435  	"version":   &StringField{},
   436  	"created":   &TimeField{},
   437  }
   438  
   439  // OffsetQueryFactory filter fields for data offsets
   440  var OffsetQueryFactory = &queryFields{
   441  	"namespace": &StringField{},
   442  	"name":      &StringField{},
   443  	"type":      &StringField{},
   444  	"current":   &Int64Field{},
   445  }
   446  
   447  // OperationQueryFactory filter fields for data operations
   448  var OperationQueryFactory = &queryFields{
   449  	"id":        &UUIDField{},
   450  	"tx":        &UUIDField{},
   451  	"type":      &StringField{},
   452  	"member":    &StringField{},
   453  	"status":    &StringField{},
   454  	"error":     &StringField{},
   455  	"plugin":    &StringField{},
   456  	"info":      &JSONField{},
   457  	"backendid": &StringField{},
   458  	"created":   &TimeField{},
   459  	"updated":   &TimeField{},
   460  }
   461  
   462  // SubscriptionQueryFactory filter fields for data subscriptions
   463  var SubscriptionQueryFactory = &queryFields{
   464  	"id":            &UUIDField{},
   465  	"namespace":     &StringField{},
   466  	"name":          &StringField{},
   467  	"transport":     &StringField{},
   468  	"events":        &StringField{},
   469  	"filter.topics": &StringField{},
   470  	"filter.tag":    &StringField{},
   471  	"filter.group":  &StringField{},
   472  	"options":       &StringField{},
   473  	"created":       &TimeField{},
   474  }
   475  
   476  // EventQueryFactory filter fields for data events
   477  var EventQueryFactory = &queryFields{
   478  	"id":        &UUIDField{},
   479  	"type":      &StringField{},
   480  	"namespace": &StringField{},
   481  	"reference": &UUIDField{},
   482  	"group":     &StringField{},
   483  	"sequence":  &Int64Field{},
   484  	"created":   &TimeField{},
   485  }
   486  
   487  // PinQueryFactory filter fields for parked contexts
   488  var PinQueryFactory = &queryFields{
   489  	"sequence":   &Int64Field{},
   490  	"masked":     &BoolField{},
   491  	"hash":       &StringField{},
   492  	"batch":      &UUIDField{},
   493  	"index":      &Int64Field{},
   494  	"dispatched": &BoolField{},
   495  	"created":    &TimeField{},
   496  }
   497  
   498  // OrganizationQueryFactory filter fields for organizations
   499  var OrganizationQueryFactory = &queryFields{
   500  	"id":          &UUIDField{},
   501  	"message":     &UUIDField{},
   502  	"parent":      &StringField{},
   503  	"identity":    &StringField{},
   504  	"description": &StringField{},
   505  	"profile":     &JSONField{},
   506  	"created":     &TimeField{},
   507  }
   508  
   509  // NodeQueryFactory filter fields for nodes
   510  var NodeQueryFactory = &queryFields{
   511  	"id":          &UUIDField{},
   512  	"message":     &UUIDField{},
   513  	"owner":       &StringField{},
   514  	"name":        &StringField{},
   515  	"description": &StringField{},
   516  	"dx.peer":     &StringField{},
   517  	"dx.endpoint": &JSONField{},
   518  	"created":     &TimeField{},
   519  }
   520  
   521  // GroupQueryFactory filter fields for nodes
   522  var GroupQueryFactory = &queryFields{
   523  	"hash":        &StringField{},
   524  	"message":     &UUIDField{},
   525  	"namespace":   &StringField{},
   526  	"description": &StringField{},
   527  	"ledger":      &UUIDField{},
   528  	"created":     &TimeField{},
   529  }
   530  
   531  // NonceQueryFactory filter fields for nodes
   532  var NonceQueryFactory = &queryFields{
   533  	"context": &StringField{},
   534  	"nonce":   &Int64Field{},
   535  	"group":   &StringField{},
   536  	"topic":   &StringField{},
   537  }
   538  
   539  // NextPinQueryFactory filter fields for nodes
   540  var NextPinQueryFactory = &queryFields{
   541  	"context":  &StringField{},
   542  	"identity": &StringField{},
   543  	"hash":     &StringField{},
   544  	"nonce":    &Int64Field{},
   545  }