vendredi 21 février 2020

Go: How to avoid dynamic assertion in wrapper functions?

In the simplified code example below, we perform tens of different API calls using the Go library from our vendor.

Vendor library provides us with a definition of API call bodies:

// body of api call A
type requestBodyA struct {
    aa string
    bb int32
}

// body of api call B
type requestBodyB struct {
    id string
}

And with functions which performs the calls for us:

// fn performs api call A
func callCreateA(body requestBodyA) {
    fmt.Println("Value of body is: ", body)
    // makes api call using body requestBodyA
}

// fn performs api call B
func callCreateB(body requestBodyB) {
    fmt.Println("Value of body is: ", body)
    // makes another api call using body requestBodyB
}

We use those to create our own CRUD functions, where we read config files and perform create/read/update/delete calls:

func createA() {
    bodyA := requestBodyA{
        aa: "asdasfsd",
        bb: 15,
    }

    // api retry loop
    for i := 1; i <= 3; i++ {
        // do some pre-checks (generic)

        callCreateA(bodyA)

        // check for errors (generic)
        // check if error is recoverable (generic)
        // if not return error (generic)
        // if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
    }
}

func createB() {
    bodyB := requestBodyB{
        id: "someUniqueId",
    }
    for i := 1; i <= 3; i++ {
        // do some pre-checks (generic)

        callCreateB(bodyB)

        // check for errors (generic)
        // check if error is recoverable (generic)
        // if not return error (generic)
        // if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
    }
}

As you can see in functions createA, createB we repeat a lot of generic code (pre-checks, error handling, retry loop and lot more) in every CRUD fn we create. As we're dealing with 30+ resources and each have their own create, read, update, destroy API call function, we have the same code copied 100+ times.

I'd like to move all the generic stuff to some genericApiCaller() and give it the call body as one param and name or a pointer to the API call function as a second param.

Problem 1: callCreateA and callCreateB needs the "body" of different types. It would be way easier if they consume interface{}, but these functions are coming from vendor SDK and it would not be smart to modify them (update reasons, etc). How to assert "body interface{}" of genericApiCaller() to "body requestBodyA" or "body requestBodyB" dynamically? Runtime assertion is not possible in Go, but I didn't find any nice solution for it.

Problem2: How to pass functions with different kinds of parameters as a parameter to genericApiCaller()?

Any idea of how to approach it will be appreciated :-) Thank you very much.

https://play.golang.org/p/incf-NEaSJ3





Aucun commentaire:

Enregistrer un commentaire