supertokens / supertokens-node

Node SDK for SuperTokens core
https://supertokens.com
Other
304 stars 81 forks source link

Allow referencing of overrided function by original implementation #199

Closed rishabhpoddar closed 3 years ago

rishabhpoddar commented 3 years ago

Right now, if the original implementation has a function A, which calls another function B, and then if the user overrides function B and then if A is called, it does not call user's B, but instead calls the original B.

This would break logic in many cases. At the moment, this affects the JS SDKs and the golang SDK.

JS fix (node, auth-react, website, react-native SDKs):

The trick here is to not use arrow functions for any of the functions in the recipe / api interface implementaiton. This allows the this variable to point to functions in the new object returned by the user solving this problem.

Then when the user calls the original function, they must bind it to this object as shown below.

Small example:

class Aa {
    m: string = "m"

    someFunc = function () {
        console.log("original someFunc", this.m)
        this.someOtherFunc()
    }

    someOtherFunc = function () {
        console.log("original someOtherFunc")
    }
}

let oI = new Aa();

let a = {
    ...oI,
    someFunc: function () {
        oI.someFunc.bind(this)();
    },
    someOtherFunc: function () {
        console.log("overrided a someOtherFunc")
    }
}

let b = {
    ...a,
    someFunc: function () {
        a.someFunc.bind(this)();
    },
    someOtherFunc: function () {
        console.log("overrided b someOtherFunc")
    }
}

b.someFunc();

Golang fix:

The trick here is to make all the recipe / api interface functions pointers to functions, and then get the user to change the pointer value when overriding it like so:

package main

import (
    "fmt"

    "github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
)

func main() {
    sI := func(email, password string) (epmodels.SignInResponse, error) {
        fmt.Println("signin")
        return epmodels.SignInResponse{}, nil
    }

    sU := func(email, password string) (epmodels.SignUpResponse, error) {
        fmt.Println("signup")
        sI("", "")
        return epmodels.SignUpResponse{}, nil
    }

    oI := epmodels.RecipeInterface{
        SignUp: &sU,
        SignIn: &sI,
    }

    originalSignIn := (*oI.SignIn)
    *(oI.SignIn) = func(email, password string) (epmodels.SignInResponse, error) {
        originalSignIn(email, password)
        fmt.Println("signin haha")
        return epmodels.SignInResponse{}, nil
    }

    (*oI.SignUp)("", "")
}

TODO:


Another important issue:

Here is the situation:

Recipe1 = {
   someFunc: () => {this.someFunc2()},
   someFunc2: () => {...}
}

recipe1 = new Recipe1()

Recipe2 = {
   someFunc: () => recipe1.someFunc(),
   someFunc3: () => recipe1.someFunc2(), // 
}

Now if a user creates an instance of Recipe2, and then overrides someFunc3 to do what they like (and semantically someFunc3 is similar to someFunc2), calling recipe2.someFunc() will not have the same expected behaviour.

rishabhpoddar commented 3 years ago

Issue two fix for nodejs: https://github.com/supertokens/supertokens-node/commit/8a4afbb699afa4439053413356e0d37225e407bf