From 9b7223c39c238e555dec98c882a6a99dbdd4421b Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 13 Dec 2023 13:38:58 +0530 Subject: [PATCH] adds more tests and linkAccounts recipe function --- ...accountlinkingRecipeImplementation_test.go | 59 +++++++++++ supertokens/accountlinkingRecipe.go | 2 +- .../accountlinkingRecipeImplementation.go | 100 +++++++++++++++++- supertokens/main.go | 13 +++ 4 files changed, 172 insertions(+), 2 deletions(-) diff --git a/recipe/emailpassword/accountlinkingRecipeImplementation_test.go b/recipe/emailpassword/accountlinkingRecipeImplementation_test.go index d871480d..aec3f0c5 100644 --- a/recipe/emailpassword/accountlinkingRecipeImplementation_test.go +++ b/recipe/emailpassword/accountlinkingRecipeImplementation_test.go @@ -596,6 +596,65 @@ func TestCanMakePrimaryUser(t *testing.T) { assert.True(t, response.OK.WasAlreadyAPrimaryUser) } +func TestMakePrimaryFailCauseAlreadyLinkedToAnotherAccount(t *testing.T) { + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + telemetry := false + supertokens.Init(supertokens.TypeInput{ + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: "http://localhost:8080", + }, + AppInfo: supertokens.AppInfo{ + AppName: "Testing", + Origin: "http://localhost:3000", + APIDomain: "http://localhost:3001", + }, + Telemetry: &telemetry, + RecipeList: []supertokens.Recipe{ + Init(nil), + }, + }) + + epuser, err := SignUp("public", "test@gmail.com", "pass123") + if err != nil { + t.Error(err) + return + } + + user1 := convertEpUserToSuperTokensUser(epuser.OK.User) + assert.False(t, user1.IsPrimaryUser) + + epuser2, err := SignUp("public", "test2@gmail.com", "pass123") + if err != nil { + t.Error(err) + return + } + + user2 := convertEpUserToSuperTokensUser(epuser2.OK.User) + + assert.False(t, user2.IsPrimaryUser) + + _, err = supertokens.CreatePrimaryUser(user1.LoginMethods[0].RecipeUserID) + if err != nil { + t.Error(err) + return + } + _, err = supertokens.LinkAccounts(user2.LoginMethods[0].RecipeUserID, user1.ID) + if err != nil { + t.Error(err) + return + } + + createPrimaryUserResponse, err := supertokens.CreatePrimaryUser(user2.LoginMethods[0].RecipeUserID) + if err != nil { + t.Error(err) + return + } + assert.Nil(t, createPrimaryUserResponse.OK) + assert.Equal(t, createPrimaryUserResponse.RecipeUserIdAlreadyLinkedWithPrimaryUserIdError.PrimaryUserId, user1.ID) +} + // TODO: remove this function func convertEpUserToSuperTokensUser(epuser epmodels.User) supertokens.User { rUId, err := supertokens.NewRecipeUserID(epuser.ID) diff --git a/supertokens/accountlinkingRecipe.go b/supertokens/accountlinkingRecipe.go index 2e7e8254..545402e1 100644 --- a/supertokens/accountlinkingRecipe.go +++ b/supertokens/accountlinkingRecipe.go @@ -39,7 +39,7 @@ func makeAccountLinkingRecipe(recipeId string, appInfo NormalisedAppinfo, config if err != nil { return AccountLinkingRecipe{}, err } - recipeImplementation := makeRecipeImplementation(*querierInstance) + recipeImplementation := makeRecipeImplementation(*querierInstance, verifiedConfig) r.RecipeImpl = verifiedConfig.Override.Functions(recipeImplementation) recipeModuleInstance := MakeRecipeModule(recipeId, appInfo, r.handleAPIRequest, r.getAllCORSHeaders, r.getAPIsHandled, nil, r.handleError, onSuperTokensAPIError) diff --git a/supertokens/accountlinkingRecipeImplementation.go b/supertokens/accountlinkingRecipeImplementation.go index 2b3467de..8f01f0e4 100644 --- a/supertokens/accountlinkingRecipeImplementation.go +++ b/supertokens/accountlinkingRecipeImplementation.go @@ -17,11 +17,12 @@ package supertokens import ( "encoding/json" + "errors" "strconv" "strings" ) -func makeRecipeImplementation(querier Querier) AccountLinkingRecipeInterface { +func makeRecipeImplementation(querier Querier, config AccountLinkingTypeNormalisedInput) AccountLinkingRecipeInterface { getUsers := func(tenantID string, timeJoinedOrder string, paginationToken *string, limit *int, includeRecipeIds *[]string, searchParams map[string]string, userContext UserContext) (UserPaginationResult, error) { requestBody := map[string]string{} @@ -182,11 +183,108 @@ func makeRecipeImplementation(querier Querier) AccountLinkingRecipeInterface { } } + linkAccounts := func(recipeUserId RecipeUserID, primaryUserId string, userContext UserContext) (LinkAccountResponse, error) { + requestBody := map[string]interface{}{ + "recipeUserId": recipeUserId.GetAsString(), + "primaryUserId": primaryUserId, + } + resp, err := querier.SendPostRequest("/recipe/accountlinking/user/link", requestBody, userContext) + + if err != nil { + return LinkAccountResponse{}, err + } + + if resp["status"].(string) == "OK" { + var user = User{} + temporaryVariable, err := json.Marshal(resp["user"]) + if err != nil { + return LinkAccountResponse{}, err + } + err = json.Unmarshal(temporaryVariable, &user) + if err != nil { + return LinkAccountResponse{}, err + } + response := LinkAccountResponse{ + OK: &struct { + AccountsAlreadyLinked bool + User User + }{ + AccountsAlreadyLinked: resp["accountsAlreadyLinked"].(bool), + User: user, + }, + } + + // TODO: call verifyEmailForRecipeUserIfLinkedAccountsAreVerified + + updatedUser, err := GetUser(user.ID, userContext) + if err != nil { + return LinkAccountResponse{}, err + } + if updatedUser == nil { + return LinkAccountResponse{}, errors.New("this should never be thrown") + } + response.OK.User = *updatedUser + var loginMethod *LoginMethods = nil + for _, method := range response.OK.User.LoginMethods { + if method.RecipeUserID.GetAsString() == recipeUserId.GetAsString() { + loginMethod = &method + break + } + } + + if loginMethod == nil { + return LinkAccountResponse{}, errors.New("this should never be thrown") + } + + err = config.OnAccountLinked(response.OK.User, loginMethod.RecipeLevelUser, userContext) + if err != nil { + return LinkAccountResponse{}, err + } + + return response, nil + } else if resp["status"].(string) == "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" { + var user = User{} + temporaryVariable, err := json.Marshal(resp["user"]) + if err != nil { + return LinkAccountResponse{}, err + } + err = json.Unmarshal(temporaryVariable, &user) + if err != nil { + return LinkAccountResponse{}, err + } + + return LinkAccountResponse{ + RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdError: &struct { + PrimaryUserId string + User User + }{ + PrimaryUserId: resp["primaryUserId"].(string), + User: user, + }, + }, nil + } else if resp["status"].(string) == "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" { + return LinkAccountResponse{ + AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdError: &struct { + PrimaryUserId string + Description string + }{ + PrimaryUserId: resp["primaryUserId"].(string), + Description: resp["description"].(string), + }, + }, nil + } else { + return LinkAccountResponse{ + InputUserIsNotAPrimaryUserError: &struct{}{}, + }, nil + } + } + // TODO:... return AccountLinkingRecipeInterface{ GetUsersWithSearchParams: &getUsers, GetUser: &getUser, CanCreatePrimaryUser: &canCreatePrimaryUser, CreatePrimaryUser: &createPrimaryUser, + LinkAccounts: &linkAccounts, } } diff --git a/supertokens/main.go b/supertokens/main.go index 960e2748..88ca7399 100644 --- a/supertokens/main.go +++ b/supertokens/main.go @@ -147,3 +147,16 @@ func CreatePrimaryUser(recipeUserId RecipeUserID, userContext ...UserContext) (C return (*accountLinkingInstance.RecipeImpl.CreatePrimaryUser)(recipeUserId, userContext[0]) } + +func LinkAccounts(recipeUserId RecipeUserID, primaryUserId string, userContext ...UserContext) (LinkAccountResponse, error) { + accountLinkingInstance, err := getAccountLinkingRecipeInstanceOrThrowError() + if err != nil { + return LinkAccountResponse{}, err + } + + if len(userContext) == 0 { + userContext = append(userContext, &map[string]interface{}{}) + } + + return (*accountLinkingInstance.RecipeImpl.LinkAccounts)(recipeUserId, primaryUserId, userContext[0]) +}