Skip to content

Commit

Permalink
Merge pull request #426 from deepjyoti30Alt/feat/form-valiadation-imp…
Browse files Browse the repository at this point in the history
…rovements-for-proper-optional-field

Improve form field validation to make optional truly optional
  • Loading branch information
rishabhpoddar authored Sep 5, 2024
2 parents f2c10d0 + ad1bdf0 commit 6e12269
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 26 deletions.
33 changes: 22 additions & 11 deletions recipe/emailpassword/api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ func validateFormFieldsOrThrowError(configFormFields []epmodels.NormalisedFormFi

func validateFormOrThrowError(configFormFields []epmodels.NormalisedFormField, inputs []epmodels.TypeFormField, tenantId string) error {
var validationErrors []errors.ErrorPayload
if len(configFormFields) != len(inputs) {
if len(configFormFields) < len(inputs) {
return supertokens.BadInputError{
Msg: "Are you sending too many / too few formFields?",
Msg: "Are you sending too many formFields?",
}
}
for _, field := range configFormFields {
Expand All @@ -105,16 +105,27 @@ func validateFormOrThrowError(configFormFields []epmodels.NormalisedFormField, i
break
}
}
if input.Value == "" && !field.Optional {

isValidInput := input.Value != ""

// If the field is not option and input is invalid, we should
// throw a validation error.
if !isValidInput && !field.Optional {
validationErrors = append(validationErrors, errors.ErrorPayload{ID: field.ID, ErrorMsg: "Field is not optional"})
} else {
err := field.Validate(input.Value, tenantId)
if err != nil {
validationErrors = append(validationErrors, errors.ErrorPayload{
ID: field.ID,
ErrorMsg: *err,
})
}
}

// If the input is invalid, we don't need to do anything
// as execution will reach here if field is optional.
if !isValidInput {
continue
}

err := field.Validate(input.Value, tenantId)
if err != nil {
validationErrors = append(validationErrors, errors.ErrorPayload{
ID: field.ID,
ErrorMsg: *err,
})
}
}
if len(validationErrors) != 0 {
Expand Down
205 changes: 190 additions & 15 deletions recipe/emailpassword/authFlow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,14 +1041,15 @@ func TestFormFieldsHasNoEmailField(t *testing.T) {

resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, 200, resp.StatusCode)

err = json.Unmarshal(dataInBytes1, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))

assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "email", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))
}

func TestFormFieldsHasNoPasswordField(t *testing.T) {
Expand Down Expand Up @@ -1130,12 +1131,14 @@ func TestFormFieldsHasNoPasswordField(t *testing.T) {

resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, 200, resp.StatusCode)
err = json.Unmarshal(dataInBytes1, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))
assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "password", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))

}

Expand Down Expand Up @@ -2343,14 +2346,15 @@ func TestFormFieldsAddedInConfigButNotInInputToSignupCheckErrorAboutItBeingMissi
t.Error(err.Error())
}
res.Body.Close()
assert.Equal(t, 400, res.StatusCode)
assert.Equal(t, 200, res.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))

assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "testField", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))
}

func TestBadCaseInputWithoutOtional(t *testing.T) {
Expand Down Expand Up @@ -2441,6 +2445,86 @@ func TestBadCaseInputWithoutOtional(t *testing.T) {

}

func TestOptionalInputFieldDoesNotThrowError(t *testing.T) {
optionalVal := true
configValue := supertokens.TypeInput{
Supertokens: &supertokens.ConnectionInfo{
ConnectionURI: "http://localhost:8080",
},
AppInfo: supertokens.AppInfo{
APIDomain: "api.supertokens.io",
AppName: "SuperTokens",
WebsiteDomain: "supertokens.io",
},
RecipeList: []supertokens.Recipe{
Init(&epmodels.TypeInput{
SignUpFeature: &epmodels.TypeInputSignUp{
FormFields: []epmodels.TypeInputFormField{
{
ID: "testField2",
Optional: &optionalVal,
},
},
},
}),
session.Init(&sessmodels.TypeInput{
GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod {
return sessmodels.CookieTransferMethod
},
}),
},
}

BeforeEach()
unittesting.StartUpST("localhost", "8080")
defer AfterEach()
err := supertokens.Init(configValue)
if err != nil {
t.Error(err.Error())
}
mux := http.NewServeMux()
testServer := httptest.NewServer(supertokens.Middleware(mux))
defer testServer.Close()

formFields := map[string][]map[string]string{
"formFields": {
{
"id": "password",
"value": "validpass123",
},
{
"id": "email",
"value": "[email protected]",
},
},
}

postBody, err := json.Marshal(formFields)
if err != nil {
t.Error(err.Error())
}

resp, err := http.Post(testServer.URL+"/auth/signup", "application/json", bytes.NewBuffer(postBody))

if err != nil {
t.Error(err.Error())
}

dataInBytes, err := io.ReadAll(resp.Body)
if err != nil {
t.Error(err.Error())
}
resp.Body.Close()

assert.Equal(t, 200, resp.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "OK", data["status"].(string))
}

func TestGoodCaseInputWithOtional(t *testing.T) {
optionalVal := true
configValue := supertokens.TypeInput{
Expand Down Expand Up @@ -2587,14 +2671,15 @@ func TestInputFormFieldWithoutEmailField(t *testing.T) {
}
resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, 200, resp.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))

assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "email", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))
}

func TestInputFormFieldWithoutPasswordField(t *testing.T) {
Expand Down Expand Up @@ -2654,13 +2739,15 @@ func TestInputFormFieldWithoutPasswordField(t *testing.T) {
}
resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, 200, resp.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))
assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "password", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))
}

func TestInputFormFieldHasADifferentNumberOfCustomFiledsThanInConfigFormFields(t *testing.T) {
Expand Down Expand Up @@ -2741,13 +2828,15 @@ func TestInputFormFieldHasADifferentNumberOfCustomFiledsThanInConfigFormFields(t
}
resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
assert.Equal(t, 200, resp.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many / too few formFields?", data["message"].(string))
assert.Equal(t, "FIELD_ERROR", data["status"].(string))
assert.Equal(t, 1, len(data["formFields"].([]interface{})))
assert.Equal(t, "testField2", (data["formFields"].([]interface{}))[0].(map[string]interface{})["id"].(string))

}

Expand Down Expand Up @@ -3177,3 +3266,89 @@ func TestSignUpAPIWorksWhenInputIsFine(t *testing.T) {
assert.Equal(t, "OK", result["status"])
assert.Equal(t, "[email protected]", result["user"].(map[string]interface{})["email"])
}

func TestInputFormFieldHasMoreNumberOfCustomFiledsThanInConfigFormFields(t *testing.T) {
configValue := supertokens.TypeInput{
Supertokens: &supertokens.ConnectionInfo{
ConnectionURI: "http://localhost:8080",
},
AppInfo: supertokens.AppInfo{
APIDomain: "api.supertokens.io",
AppName: "SuperTokens",
WebsiteDomain: "supertokens.io",
},
RecipeList: []supertokens.Recipe{
Init(&epmodels.TypeInput{
SignUpFeature: &epmodels.TypeInputSignUp{
FormFields: []epmodels.TypeInputFormField{
{
ID: "testField2",
},
},
},
}),
session.Init(&sessmodels.TypeInput{
GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod {
return sessmodels.CookieTransferMethod
},
}),
},
}

BeforeEach()
unittesting.StartUpST("localhost", "8080")
defer AfterEach()
err := supertokens.Init(configValue)
if err != nil {
t.Error(err.Error())
}
mux := http.NewServeMux()
testServer := httptest.NewServer(supertokens.Middleware(mux))
defer testServer.Close()

formFields := map[string][]map[string]string{
"formFields": {
{
"id": "password",
"value": "validpass123",
},
{
"id": "email",
"value": "[email protected]",
},
{
"id": "testField",
"value": "",
},
{
"id": "testField2",
"value": "",
},
},
}

postBody, err := json.Marshal(formFields)
if err != nil {
t.Error(err.Error())
}

resp, err := http.Post(testServer.URL+"/auth/signup", "application/json", bytes.NewBuffer(postBody))

if err != nil {
t.Error(err.Error())
}

dataInBytes, err := io.ReadAll(resp.Body)
if err != nil {
t.Error(err.Error())
}
resp.Body.Close()

assert.Equal(t, 400, resp.StatusCode)
var data map[string]interface{}
err = json.Unmarshal(dataInBytes, &data)
if err != nil {
t.Error(err.Error())
}
assert.Equal(t, "Are you sending too many formFields?", data["message"].(string))
}

0 comments on commit 6e12269

Please sign in to comment.