package qsl import ( "fmt" "os" "time" uuid "github.com/satori/go.uuid" "gitlab-app.eng.qops.net/golang/qmp/qsl/internal/qm" "gitlab-app.eng.qops.net/golang/qmp/qsl/internal/schema" "gitlab-app.eng.qops.net/golang/qmp/qsl/internal/schema/registry" ) const hostName = "HOSTNAME" const defaultHost = "unknown" // qmWriter implements the qsl's Writer interface. // It creates writing Messages. type qmWriter struct { schemaID string writerSchemaSpec string writerApp string manager writerSchemaManager } type writerSchemaManager interface { Get(string) (string, error) } // newWriter creats a new Message writer generator for a schema. func newWriter(writerSchemaSpec, schemaID, writerApp string, schemaManager writerSchemaManager) *qmWriter { return &qmWriter{ writerSchemaSpec: writerSchemaSpec, manager: schemaManager, writerApp: writerApp, schemaID: schemaID, } } // NewFromMessage creates a new Message from a reading Message // with the Writer's schema. func (q *qmWriter) NewFromMessage(msg Message) (Message, error) { if err := schema.IsCompatible(q.writerSchemaSpec, msg.GetWriterSchema()); err != nil { return nil, err } originalBytes, err := msg.GetOriginalBytes() if err != nil { return nil, err } qMsg, err := qm.NewReadingMessage(msg.GetWriterSchema(), q.writerSchemaSpec, originalBytes) if err != nil { return nil, err } qMsg.UseOriginalBytes(false) qMsg.SetWriterSchema(q.schemaID, q.writerSchemaSpec) return qMsg, nil } // NewMessage attempts to create a new Message for writing using // the qmWriter's schema. func (q *qmWriter) NewMessage() (Message, error) { writerHost := os.Getenv(hostName) if writerHost == "" { writerHost = defaultHost } messageID := uuid.NewV4().String() return qm.NewWritingMessage(q.writerSchemaSpec, q.schemaID, messageID, makeTimestamp(), writerHost, q.writerApp) } // NewFromObject attempts to create a new Message for writing using // the given initialized object, checking if it is compatible with the Writer's // schema. func (q *qmWriter) NewFromObject(avroObject map[string]interface{}) (Message, error) { objSchema, schemaID, err := schemaIfCompatible(avroObject, q.manager, q.writerSchemaSpec) if err != nil { return nil, err } writerHost := os.Getenv(hostName) if writerHost == "" { writerHost = defaultHost } messageID := uuid.NewV4().String() qMsg, err := qm.NewWritingMessage(objSchema, schemaID, messageID, makeTimestamp(), writerHost, q.writerApp) if err != nil { return nil, err } qMsg.SetWriterSchema(q.schemaID, q.writerSchemaSpec) for k, v := range avroObject { qMsg.GetAvroObject()[k] = v } if _, err := qMsg.GetSerializedBytes(); err != nil { return nil, err } return qMsg, nil } func makeTimestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } func schemaIfCompatible(avroObject map[string]interface{}, manager writerSchemaManager, readerSchemaSpec string) (string, string, error) { schemaIDValue, ok := avroObject[registry.SchemaID] if !ok { return "", "", fmt.Errorf("avro object does not have %q", registry.SchemaID) } schemaID, ok := schemaIDValue.(string) if !ok { return "", "", fmt.Errorf("avro object has non-string %q", registry.SchemaID) } writerSchemaSpec, err := manager.Get(schemaID) if err != nil { return "", "", err } if err = schema.IsCompatible(writerSchemaSpec, readerSchemaSpec); err != nil { return "", "", err } return writerSchemaSpec, schemaID, nil }