jeudi 13 février 2020

Insert data with Gorm with reflect

I'm creating a basic REST service. My intent is to write the logic for the resources as abstractly as possible. What I mean is if I have already created a CRUD logic for endpoint /devices for example, then when I need a new resource endpoint like /cars, I should not be repeating myself over the CRUD procedures.

In another language like Python, classes and methods are first class objects and that can be stored in a list or dictionary (map) and then instantiated as needed. In Go it doesn't seem as easy. I tried to use the reflect package.

First I create a TypeRegistry according to this.

var TypeRegistry = make(map[string]reflect.Type)
TypeRegistry["devices"] = reflect.TypeOf(models.Device{}) // models.Device{} is the Gorm SQL table model

Then I have handler creator which is intended to handle the creation of all types of resources like this (error handling redacted):

func CreateOneHandler(typeString string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        defer r.Body.Close()

        jsn, _ = ioutil.ReadAll(r.Body)
        jsonBytes, _ := datamapper.CreateOne(typeString, jsn)

        w.Write(jsonBytes)
    }
}

I'm using Chi, so I bind the handlers like this:

func addRoute(r chi.Router, endpoint string, typeString string) {
    r.Route("/"+endpoint, func(r chi.Router) {
        typeString := endpoint
        r.Post("/", CreateOneHandler(typeString))
    })
}

The idea is to, after defining the Gorm models, simply add routes by calling it repeatedly, addRoute(r, "devices"); addRoute(r, "cars") for a consistent REST interface across multiple models.

Now within CreateOne() I want to insert something into the table:

func CreateOne(typeString string, json []byte) ([]byte, error) {
    modelType := typeregistry.TypeRegistry[typeString]
    value := reflect.New(modelType)
    db.Create(modelPtr.Elem()) // ==> Now this doesn't work 
}

How do I make it work? Gorm said "create failed no such table: value". Because a reflect value or reflect type isn't the same as if I were just to instantiate objects the regular way. How do I make it work?

(A side note: given the static nature of the type switch and type assertions, I am already compromising some of my designs which would probably be possible in a language like Python. It seems to me like it's unavoidable to litter code with type switches which tried to check whether it is a device, car or any number of new models explicitly. In a regular object-oriented language maybe this would be simple polymorphic method call. Any pointer to better design would be appreciated as well.)





Aucun commentaire:

Enregistrer un commentaire