package cushion import ( "fmt" "regexp" "strconv" "strings" "edge-infra.dev/pkg/edge/apis/meta" "edge-infra.dev/pkg/edge/datasync/couchdb" ) var ( couchdbValidName = regexp.MustCompile(`^[a-z][a-z0-9_$()+/-]*$`) ) const ( OperationCreate = "CREATE" OperationDelete = "DELETE" ) // Request is a data-sync pubsub message type Request struct { // todo - need to filter on tenant_id, maybe just need a subscription per banner // TenantID is the BSL organization the message is meant for. Right now assumed child org TenantID string `json:"tenant_id"` // DBName is the name of the CouchDB the data should go into. Services integrated with data-sync // will use the same name when reading from store CouchDB instance. DBName string `json:"db_name"` // EntityID is a unique ID specific to the entity in the message. Will be used as docID in CouchDB. EntityID string `json:"entity_id"` // Deleted is a flag to determine if the entity should be deleted Deleted bool `json:"deleted"` // todo - wire up // Provider is the name of the data provider associated with a database Provider string `json:"provider"` // SiteID is unique id representing the store to replicate to SiteID string `json:"site_id"` // TouchpointID is unique id representing the touchpoint to replicate to TouchpointID string `json:"touchpoint_id"` // EnterpriseUnitID is the bsl unit id, can also be found in the store bsl info configmap EnterpriseUnitID string `json:"enterprise_unit_id"` // todo - want to replace deleted with this on next round // Operation string `json:"operation"` // ParentID is not used right now as we're assuming all messages are coming from child orgs ParentID string `json:"parent_id"` // EntityType is not used right now, maybe useful to add contextual information in logs EntityType string `json:"entity_type"` // Version represents the version the entity, not sure what it's purpose is Version string `json:"version"` // Credentials use for deleting database by ps users Credentials string `json:"credentials"` } // Validate checks if the request is valid // DBName if error is nil, CouchDB database name is correct // TODO add regex validation for other fields in another PR func (r *Request) Validate() error { // commenting out till we are able to get to the point where we can make changes we want // if r.Operation == "" { // return fmt.Errorf("operation must not be empty") // } // switch r.Operation { // case OperationCreate: // case OperationDelete: // default: // return fmt.Errorf("unsupported Operation %q", r.Operation) // } if r.TenantID == "" { return fmt.Errorf("tenant_id message attribute must not be empty") } if r.DBName == "" { return fmt.Errorf("db_name message attribute must not be empty") } r.DBName = normalizeDBName(r.DBName) // backward compatibility if !couchdbValidName.MatchString(r.DBName) { return fmt.Errorf("db name: %s not matching regex: ^[a-z][a-z0-9_$()+/-]*$", r.DBName) } if r.EntityID == "" { return fmt.Errorf("entity_id message attribute must not be empty") } return nil } func (r *Request) FromAttributes(attrs map[string]string) error { // could look into using reflect to do this a little more intelligently // r.Operation = attrs["operation"] r.TenantID = attrs["tenant_id"] r.DBName = attrs["db_name"] r.EntityID = attrs["entity_id"] deletedStr := attrs["deleted"] if deletedStr == "" { r.Deleted = false } else { var err error r.Deleted, err = strconv.ParseBool(deletedStr) if err != nil { return err } } r.ParentID = attrs["parent_id"] r.EntityType = attrs["entity_type"] r.Version = attrs["version"] r.Provider = attrs["provider"] r.SiteID = attrs["site_id"] r.TouchpointID = attrs["touchpoint_id"] r.EnterpriseUnitID = attrs["enterprise_unit_id"] // can we just bind this to site id? return r.Validate() } // K8sDBName returns the name of the k8s CouchDBDatabase resource func (r *Request) K8sDBName() string { return K8sDBName(r.DBName) } // normalizeDBName couchdb db names are lowercase // Note: This only exists for backward compatibility func normalizeDBName(dbname string) string { return strings.ToLower(dbname) } // K8sDBName normalize and avoid name collision for k8s resources func K8sDBName(name string) string { return fmt.Sprintf("%s-%s", couchdb.CouchDBName, meta.Hash(normalizeDBName(name))) }