...

Source file src/edge-infra.dev/pkg/edge/datasync/cushion/request.go

Documentation: edge-infra.dev/pkg/edge/datasync/cushion

     1  package cushion
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"edge-infra.dev/pkg/edge/apis/meta"
    10  	"edge-infra.dev/pkg/edge/datasync/couchdb"
    11  )
    12  
    13  var (
    14  	couchdbValidName = regexp.MustCompile(`^[a-z][a-z0-9_$()+/-]*$`)
    15  )
    16  
    17  const (
    18  	OperationCreate = "CREATE"
    19  	OperationDelete = "DELETE"
    20  )
    21  
    22  // Request is a data-sync pubsub message
    23  type Request struct {
    24  	// todo - need to filter on tenant_id, maybe just need a subscription per banner
    25  	// TenantID is the BSL organization the message is meant for. Right now assumed child org
    26  	TenantID string `json:"tenant_id"`
    27  	// DBName is the name of the CouchDB the data should go into. Services integrated with data-sync
    28  	// will use the same name when reading from store CouchDB instance.
    29  	DBName string `json:"db_name"`
    30  	// EntityID is a unique ID specific to the entity in the message. Will be used as docID in CouchDB.
    31  	EntityID string `json:"entity_id"`
    32  	// Deleted is a flag to determine if the entity should be deleted
    33  	Deleted bool `json:"deleted"` // todo - wire up
    34  	// Provider is the name of the data provider associated with a database
    35  	Provider string `json:"provider"`
    36  	// SiteID is unique id representing the store to replicate to
    37  	SiteID string `json:"site_id"`
    38  	// TouchpointID is unique id representing the touchpoint to replicate to
    39  	TouchpointID string `json:"touchpoint_id"`
    40  
    41  	// EnterpriseUnitID is the bsl unit id, can also be found in the store bsl info configmap
    42  	EnterpriseUnitID string `json:"enterprise_unit_id"`
    43  
    44  	// todo - want to replace deleted with this on next round
    45  	// Operation string `json:"operation"`
    46  
    47  	// ParentID is not used right now as we're assuming all messages are coming from child orgs
    48  	ParentID string `json:"parent_id"`
    49  	// EntityType is not used right now, maybe useful to add contextual information in logs
    50  	EntityType string `json:"entity_type"`
    51  	// Version represents the version the entity, not sure what it's purpose is
    52  	Version string `json:"version"`
    53  
    54  	// Credentials use for deleting database by ps users
    55  	Credentials string `json:"credentials"`
    56  }
    57  
    58  // Validate checks if the request is valid
    59  // DBName if error is nil, CouchDB database name is correct
    60  // TODO add regex validation for other fields in another PR
    61  func (r *Request) Validate() error {
    62  	// commenting out till we are able to get to the point where we can make changes we want
    63  	// if r.Operation == "" {
    64  	// 	return fmt.Errorf("operation must not be empty")
    65  	// }
    66  
    67  	// switch r.Operation {
    68  	// case OperationCreate:
    69  	// case OperationDelete:
    70  	// default:
    71  	// 	return fmt.Errorf("unsupported Operation %q", r.Operation)
    72  	// }
    73  
    74  	if r.TenantID == "" {
    75  		return fmt.Errorf("tenant_id message attribute must not be empty")
    76  	}
    77  
    78  	if r.DBName == "" {
    79  		return fmt.Errorf("db_name message attribute must not be empty")
    80  	}
    81  	r.DBName = normalizeDBName(r.DBName) // backward compatibility
    82  	if !couchdbValidName.MatchString(r.DBName) {
    83  		return fmt.Errorf("db name: %s not matching regex: ^[a-z][a-z0-9_$()+/-]*$", r.DBName)
    84  	}
    85  
    86  	if r.EntityID == "" {
    87  		return fmt.Errorf("entity_id message attribute must not be empty")
    88  	}
    89  	return nil
    90  }
    91  
    92  func (r *Request) FromAttributes(attrs map[string]string) error {
    93  	// could look into using reflect to do this a little more intelligently
    94  
    95  	// r.Operation = attrs["operation"]
    96  	r.TenantID = attrs["tenant_id"]
    97  	r.DBName = attrs["db_name"]
    98  	r.EntityID = attrs["entity_id"]
    99  
   100  	deletedStr := attrs["deleted"]
   101  	if deletedStr == "" {
   102  		r.Deleted = false
   103  	} else {
   104  		var err error
   105  		r.Deleted, err = strconv.ParseBool(deletedStr)
   106  		if err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	r.ParentID = attrs["parent_id"]
   112  	r.EntityType = attrs["entity_type"]
   113  	r.Version = attrs["version"]
   114  	r.Provider = attrs["provider"]
   115  	r.SiteID = attrs["site_id"]
   116  	r.TouchpointID = attrs["touchpoint_id"]
   117  	r.EnterpriseUnitID = attrs["enterprise_unit_id"] // can we just bind this to site id?
   118  
   119  	return r.Validate()
   120  }
   121  
   122  // K8sDBName returns the name of the k8s CouchDBDatabase resource
   123  func (r *Request) K8sDBName() string {
   124  	return K8sDBName(r.DBName)
   125  }
   126  
   127  // normalizeDBName couchdb db names are lowercase
   128  // Note: This only exists for backward compatibility
   129  func normalizeDBName(dbname string) string {
   130  	return strings.ToLower(dbname)
   131  }
   132  
   133  // K8sDBName normalize and avoid name collision for k8s resources
   134  func K8sDBName(name string) string {
   135  	return fmt.Sprintf("%s-%s", couchdb.CouchDBName, meta.Hash(normalizeDBName(name)))
   136  }
   137  

View as plain text