feat(input): Full refacto of the user input system
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
a992c3cd5e
commit
dccbb30a85
15
cmd/init.go
15
cmd/init.go
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/helpers/api_types"
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers/api_types"
|
||||||
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers/input"
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -99,7 +100,7 @@ func runInitAction(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Which kind of API do you want to init (possible values: %s)", strings.Join(possibleAPITypes, ", "))
|
log.Infof("Which kind of API do you want to init (possible values: %s)", strings.Join(possibleAPITypes, ", "))
|
||||||
apiTypeName := helpers.APITypeNameInput()
|
apiTypeName := input.APITypeName(true)
|
||||||
|
|
||||||
log.Debugf("Using api type : %s", string(apiTypeName))
|
log.Debugf("Using api type : %s", string(apiTypeName))
|
||||||
|
|
||||||
@ -120,27 +121,27 @@ func runInitAction(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
// ask project directory
|
// ask project directory
|
||||||
log.Info("Name of the project directory:")
|
log.Info("Name of the project directory:")
|
||||||
projectDirectory := helpers.AlphanumericalInput()
|
projectDirectory := input.Alphanumerical(true)
|
||||||
|
|
||||||
// ask project name
|
// ask project name
|
||||||
log.Info("Name of the project:")
|
log.Info("Name of the project:")
|
||||||
config.ProjectName = helpers.AlphanumericalAndSpaceInput()
|
config.ProjectName = input.AlphanumericalAndSpace(true)
|
||||||
|
|
||||||
// ask project description
|
// ask project description
|
||||||
log.Info("Description of the project:")
|
log.Info("Description of the project:")
|
||||||
config.ProjectDescription = helpers.StringInput()
|
config.ProjectDescription = input.String(true)
|
||||||
|
|
||||||
// ask project contact
|
// ask project contact
|
||||||
config.ProjectContact = models.ProjectContactStruct{}
|
config.ProjectContact = models.ProjectContactStruct{}
|
||||||
|
|
||||||
log.Info("Mail address of the developer team:")
|
log.Info("Mail address of the developer team:")
|
||||||
config.ProjectContact.Email = helpers.StringInput()
|
config.ProjectContact.Email = input.Mail(true)
|
||||||
|
|
||||||
log.Info("Name of the team:")
|
log.Info("Name of the team:")
|
||||||
config.ProjectContact.Name = helpers.StringInput()
|
config.ProjectContact.Name = input.String(true)
|
||||||
|
|
||||||
log.Info("URL of the contact informatino of the team:")
|
log.Info("URL of the contact informatino of the team:")
|
||||||
config.ProjectContact.URL = helpers.StringInput()
|
config.ProjectContact.URL = input.String(false)
|
||||||
|
|
||||||
// launch GetInitializeUserInput from selected api type
|
// launch GetInitializeUserInput from selected api type
|
||||||
log.Debug("Get user input for the selected API type")
|
log.Debug("Get user input for the selected API type")
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers/input"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -55,7 +56,7 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
|
|||||||
if !acceptAll {
|
if !acceptAll {
|
||||||
log.Infof("Do you want to install the following version: %s", dependency.GetMinimumVersion())
|
log.Infof("Do you want to install the following version: %s", dependency.GetMinimumVersion())
|
||||||
|
|
||||||
answer := helpers.YesOrNoInput()
|
answer := input.YesOrNo()
|
||||||
if !answer {
|
if !answer {
|
||||||
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
||||||
continue
|
continue
|
||||||
@ -74,10 +75,10 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
|
|||||||
if !acceptAll {
|
if !acceptAll {
|
||||||
log.Infof("Do you want to install %s in the directory %s ?", dependency.GetName(), installDirectory)
|
log.Infof("Do you want to install %s in the directory %s ?", dependency.GetName(), installDirectory)
|
||||||
|
|
||||||
answer := helpers.YesOrNoInput()
|
answer := input.YesOrNo()
|
||||||
if !answer {
|
if !answer {
|
||||||
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
||||||
installDirectory = helpers.PathInput()
|
installDirectory = input.Path(true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Installing %s in the following directory: %s", dependency.GetName(), installDirectory)
|
log.Infof("Installing %s in the following directory: %s", dependency.GetName(), installDirectory)
|
||||||
@ -93,12 +94,12 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
||||||
installDirectory = helpers.PathInput()
|
installDirectory = input.Path(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%s", dependency.DescribeInstall(installDirectory))
|
log.Infof("%s", dependency.DescribeInstall(installDirectory))
|
||||||
log.Info("Do you want to continue ?")
|
log.Info("Do you want to continue ?")
|
||||||
answer := helpers.YesOrNoInput()
|
answer := input.YesOrNo()
|
||||||
if !answer {
|
if !answer {
|
||||||
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
||||||
continue
|
continue
|
||||||
@ -114,7 +115,7 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
log.Infof("%s", dependency.DescribePostInstall(installDirectory))
|
log.Infof("%s", dependency.DescribePostInstall(installDirectory))
|
||||||
log.Info("Do you want to continue ?")
|
log.Info("Do you want to continue ?")
|
||||||
answer = helpers.YesOrNoInput()
|
answer = input.YesOrNo()
|
||||||
if !answer {
|
if !answer {
|
||||||
log.Warnf("Skipping post installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
log.Warnf("Skipping post installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
||||||
continue
|
continue
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers/input"
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -31,13 +32,13 @@ func (a APIType) GetInitializeUserInput(params *models.Config) (*models.Config,
|
|||||||
|
|
||||||
if !isInGoPath {
|
if !isInGoPath {
|
||||||
log.Debug("We are not in GoPath, ask extra info")
|
log.Debug("We are not in GoPath, ask extra info")
|
||||||
goModulePath := helpers.StringInput()
|
goModulePath := input.String(true)
|
||||||
log.Info("Go Module path:")
|
log.Info("Go Module path:")
|
||||||
params.ModulePath = &goModulePath
|
params.ModulePath = &goModulePath
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Do you want to enable database models auto generation ?")
|
log.Info("Do you want to enable database models auto generation ?")
|
||||||
params.Features.DatabaseModels.Enabled = helpers.YesOrNoInput()
|
params.Features.DatabaseModels.Enabled = input.YesOrNo()
|
||||||
|
|
||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
158
helpers/input.go
158
helpers/input.go
@ -1,158 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// YesOrNoInput ask user for a yes or no reply and try until we get a possible answer.
|
|
||||||
func YesOrNoInput() bool {
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
lUserInput := strings.ToLower(userInput)
|
|
||||||
|
|
||||||
for _, positiveAnswer := range []string{"yes", "y", "1", "true"} {
|
|
||||||
if lUserInput == positiveAnswer {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, negativeAnswer := range []string{"no", "n", "0", "false"} {
|
|
||||||
if lUserInput == negativeAnswer {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Expecting a yes or no answer, Try again")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AlphanumericalInput ask user for a alphanumerical string and try until we get a possible answer.
|
|
||||||
func AlphanumericalInput() string {
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
if userInput == "" {
|
|
||||||
log.Warn("Empty input, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
is_alphanumeric := regexp.MustCompile(`^[a-zA-Z0-9\-\_]*$`).MatchString(userInput)
|
|
||||||
|
|
||||||
if !is_alphanumeric {
|
|
||||||
log.Warn("Please use a-z, A-Z, 0-9 and - or _ characters only.")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AlphanumericalAndSpaceInput ask user for a alphanumerical+space string and try until we get a possible answer.
|
|
||||||
func AlphanumericalAndSpaceInput() string {
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
if userInput == "" {
|
|
||||||
log.Warn("Empty input, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
is_alphanumeric := regexp.MustCompile(`^[a-zA-Z0-9\-\_\s]*$`).MatchString(userInput)
|
|
||||||
|
|
||||||
if !is_alphanumeric {
|
|
||||||
log.Warn("Please use a-z, A-Z, 0-9, - , space or _ characters only.")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringInput ask user for a string and try until we get a possible answer.
|
|
||||||
func StringInput() string {
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
if userInput == "" {
|
|
||||||
log.Warn("Empty input, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathInput ask user for a path string and try until we get a possible answer.
|
|
||||||
func PathInput() string {
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
if userInput == "" {
|
|
||||||
log.Warn("Empty input, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := CheckAndCreateDir(userInput)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("please, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// APITypeNameInput ask user for a string that is a valid API Type name and try until we get a possible answer.
|
|
||||||
func APITypeNameInput() models.APITypeName {
|
|
||||||
|
|
||||||
possibleAPITypes := make([]string, 0)
|
|
||||||
for _, apiType := range models.GetListOfAPITypeName() {
|
|
||||||
possibleAPITypes = append(possibleAPITypes, string(apiType))
|
|
||||||
}
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for {
|
|
||||||
|
|
||||||
scanner.Scan()
|
|
||||||
userInput := scanner.Text()
|
|
||||||
|
|
||||||
if userInput == "" {
|
|
||||||
log.Warn("Empty input, try again")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
apiTypeName, err := models.NewAPITypeNameFromInput(userInput)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("invalid API type (possible values: %s)", strings.Join(possibleAPITypes, ", "))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiTypeName
|
|
||||||
}
|
|
||||||
}
|
|
29
helpers/input/bool.go
Normal file
29
helpers/input/bool.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// YesOrNo ask user for a yes or no reply and try until we get a possible answer.
|
||||||
|
func YesOrNo() bool {
|
||||||
|
|
||||||
|
value := Ask(true, func(in string) (string, error) {
|
||||||
|
lUserInput := strings.ToLower(in)
|
||||||
|
|
||||||
|
for _, positiveAnswer := range []string{"yes", "y", "1", "true"} {
|
||||||
|
if lUserInput == positiveAnswer {
|
||||||
|
return "y", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, negativeAnswer := range []string{"no", "n", "0", "false"} {
|
||||||
|
if lUserInput == negativeAnswer {
|
||||||
|
return "n", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("expecting a yes or no answer, try again")
|
||||||
|
})
|
||||||
|
|
||||||
|
return value == "y"
|
||||||
|
}
|
40
helpers/input/input.go
Normal file
40
helpers/input/input.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator represent signature of the input validation function.
|
||||||
|
type Validator func(in string) (string, error)
|
||||||
|
|
||||||
|
// Ask will ask user for an input and check validity with provided fn function.
|
||||||
|
func Ask(mandatory bool, fn Validator) string {
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
for {
|
||||||
|
|
||||||
|
scanner.Scan()
|
||||||
|
userInput := scanner.Text()
|
||||||
|
|
||||||
|
if userInput == "" {
|
||||||
|
|
||||||
|
if mandatory {
|
||||||
|
log.Warn("Empty input, try again")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInput
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := fn(userInput)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("%s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
51
helpers/input/other.go
Normal file
51
helpers/input/other.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.home.m-and-m.ovh/mderasse/gouick/helpers"
|
||||||
|
"git.home.m-and-m.ovh/mderasse/gouick/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Path ask user for a path string and try until we get a possible answer.
|
||||||
|
func Path(mandatory bool) string {
|
||||||
|
|
||||||
|
value := Ask(mandatory, func(in string) (string, error) {
|
||||||
|
|
||||||
|
path := filepath.Clean(in)
|
||||||
|
|
||||||
|
_, err := helpers.CheckAndCreateDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("please try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// APITypeName ask user for a string that is a valid API Type name and try until we get a possible answer.
|
||||||
|
func APITypeName(mandatory bool) models.APITypeName {
|
||||||
|
|
||||||
|
possibleAPITypes := make([]string, 0)
|
||||||
|
for _, apiType := range models.GetListOfAPITypeName() {
|
||||||
|
possibleAPITypes = append(possibleAPITypes, string(apiType))
|
||||||
|
}
|
||||||
|
|
||||||
|
value := Ask(mandatory, func(in string) (string, error) {
|
||||||
|
|
||||||
|
apiTypeName, err := models.NewAPITypeNameFromInput(in)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid API type (possible values: %s)", strings.Join(possibleAPITypes, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(apiTypeName), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
apiTypeName, _ := models.NewAPITypeName(value)
|
||||||
|
|
||||||
|
return apiTypeName
|
||||||
|
}
|
66
helpers/input/string.go
Normal file
66
helpers/input/string.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/mail"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// RegexAlphanumerical is a basic alphanumerical regex to be use in RegexInput function.
|
||||||
|
RegexAlphanumerical = regexp.MustCompile(`^[a-zA-Z0-9\-\_]*$`)
|
||||||
|
// RegexAlphanumericalAndSpace is a basic alphanumerical regex with space as an additionnal valid character to be use in RegexInput function.
|
||||||
|
RegexAlphanumericalAndSpace = regexp.MustCompile(`^[a-zA-Z0-9\-\_\s]*$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Regex permit to ask user for an input and check that it match the given regex rule.
|
||||||
|
// if not user will see the errMsg and be asked for another input.
|
||||||
|
func Regex(mandatory bool, regex *regexp.Regexp, errMsg string) string {
|
||||||
|
|
||||||
|
value := Ask(mandatory, func(in string) (string, error) {
|
||||||
|
|
||||||
|
if !regex.MatchString(in) {
|
||||||
|
return "", fmt.Errorf("%s", errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return in, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alphanumerical will ask user for an input respecting the RegexAlphanumerical.
|
||||||
|
func Alphanumerical(mandatory bool) string {
|
||||||
|
return Regex(mandatory, RegexAlphanumerical, "Please use a-z, A-Z, 0-9, - and _ characters only.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlphanumericalAndSpace will ask user for an input respecting the RegexAlphanumericalAndSpace.
|
||||||
|
func AlphanumericalAndSpace(mandatory bool) string {
|
||||||
|
return Regex(mandatory, RegexAlphanumericalAndSpace, "Please use a-z, A-Z, 0-9, -, _ and space characters only.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// String permit to ask user for any kind of string.
|
||||||
|
func String(mandatory bool) string {
|
||||||
|
|
||||||
|
value := Ask(mandatory, func(in string) (string, error) {
|
||||||
|
return in, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mail permit to ask user for an email address.
|
||||||
|
func Mail(mandatory bool) string {
|
||||||
|
|
||||||
|
value := Ask(mandatory, func(in string) (string, error) {
|
||||||
|
|
||||||
|
_, err := mail.ParseAddress(in)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("expecting a valid mail address, please try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
return in, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user