Prefix and suffix detected

This commit is contained in:
Andrey Pokhilko
2022-02-21 19:15:30 +03:00
parent e2961a49d8
commit a3357fd589
2 changed files with 78 additions and 8 deletions

View File

@@ -2,13 +2,12 @@ package oas
import ( import (
"encoding/json" "encoding/json"
"github.com/chanced/openapi"
"github.com/up9inc/mizu/shared/logger"
"net/url" "net/url"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/chanced/openapi"
"github.com/up9inc/mizu/shared/logger"
) )
type NodePath = []string type NodePath = []string
@@ -79,7 +78,7 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
logger.Log.Warningf("Failed to add example to a parameter: %s", err) logger.Log.Warningf("Failed to add example to a parameter: %s", err)
} }
if len(*exmp) > 1 && node.pathParam.Schema.Pattern == nil { // is it enough to decide on 2 samples? if len(*exmp) >= 3 && node.pathParam.Schema.Pattern == nil { // is it enough to decide on 2 samples?
node.pathParam.Schema.Pattern = getPatternFromExamples(exmp) node.pathParam.Schema.Pattern = getPatternFromExamples(exmp)
} }
} }
@@ -96,6 +95,7 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
func getPatternFromExamples(exmp *openapi.Examples) *openapi.Regexp { func getPatternFromExamples(exmp *openapi.Examples) *openapi.Regexp {
allInts := true allInts := true
strs := make([]string, 0)
for _, example := range *exmp { for _, example := range *exmp {
exampleObj, err := example.ResolveExample(exampleResolver) exampleObj, err := example.ResolveExample(exampleResolver)
if err != nil { if err != nil {
@@ -108,6 +108,7 @@ func getPatternFromExamples(exmp *openapi.Examples) *openapi.Regexp {
logger.Log.Warningf("Failed decoding parameter example into string: %s", err) logger.Log.Warningf("Failed decoding parameter example into string: %s", err)
continue continue
} }
strs = append(strs, value)
if _, err := strconv.Atoi(value); err != nil { if _, err := strconv.Atoi(value); err != nil {
allInts = false allInts = false
@@ -118,6 +119,27 @@ func getPatternFromExamples(exmp *openapi.Examples) *openapi.Regexp {
re := new(openapi.Regexp) re := new(openapi.Regexp)
re.Regexp = regexp.MustCompile(`\d+`) re.Regexp = regexp.MustCompile(`\d+`)
return re return re
} else {
prefix := longestCommonXfixStr(strs, true)
suffix := longestCommonXfixStr(strs, false)
pat := ""
separators := "-._/:|*,+"
if len(prefix) > 0 && strings.Contains(separators, string(prefix[len(prefix)-1])) {
pat = "^" + regexp.QuoteMeta(prefix)
}
pat += ".+"
if len(suffix) > 0 && strings.Contains(separators, string(suffix[0])) {
pat += regexp.QuoteMeta(suffix) + "$"
}
if pat != ".+" {
re := new(openapi.Regexp)
re.Regexp = regexp.MustCompile(`\d+`)
return re
}
} }
return nil return nil
} }
@@ -159,19 +181,20 @@ func (n *Node) searchInParams(paramObj *openapi.ParameterObj, chunk string, chun
continue continue
} }
if chunkIsGibberish { if paramObj != nil {
return subnode
} else if paramObj != nil {
// TODO: mergeParam(subnode.pathParam, paramObj) // TODO: mergeParam(subnode.pathParam, paramObj)
return subnode return subnode
} else if subnode.pathParam.Schema.Pattern != nil { } else if subnode.pathParam.Schema.Pattern != nil { // it has defined param pattern, have to respect it
// TODO: and not in exceptions // TODO: and not in exceptions
if subnode.pathParam.Schema.Pattern.Match([]byte(chunk)) { if subnode.pathParam.Schema.Pattern.Match([]byte(chunk)) {
return subnode return subnode
} else { } else {
return nil return nil
} }
} else if chunkIsGibberish {
return subnode
} }
} }
return nil return nil
} }

View File

@@ -290,6 +290,53 @@ func longestCommonXfix(strs [][]string, pre bool) []string { // https://github.c
return xfix return xfix
} }
func longestCommonXfixStr(strs []string, pre bool) string { // https://github.com/jpillora/longestcommon
//short-circuit empty list
if len(strs) == 0 {
return ""
}
xfix := strs[0]
//short-circuit single-element list
if len(strs) == 1 {
return xfix
}
//compare first to rest
for _, str := range strs[1:] {
xfixl := len(xfix)
strl := len(str)
//short-circuit empty strings
if xfixl == 0 || strl == 0 {
return ""
}
//maximum possible length
maxl := xfixl
if strl < maxl {
maxl = strl
}
//compare letters
if pre {
//prefix, iterate left to right
for i := 0; i < maxl; i++ {
if xfix[i] != str[i] {
xfix = xfix[:i]
break
}
}
} else {
//suffix, iternate right to left
for i := 0; i < maxl; i++ {
xi := xfixl - i - 1
si := strl - i - 1
if xfix[xi] != str[si] {
xfix = xfix[xi+1:]
break
}
}
}
}
return xfix
}
func getSimilarPrefix(strs []string) string { func getSimilarPrefix(strs []string) string {
chunked := make([][]string, 0) chunked := make([][]string, 0)
for _, item := range strs { for _, item := range strs {