Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

252 řádky
6.0 KiB

  1. package helper
  2. import (
  3. "fmt"
  4. "io"
  5. "mime/multipart"
  6. "strconv"
  7. "strings"
  8. "github.com/QuantumNous/new-api/common"
  9. "github.com/QuantumNous/new-api/types"
  10. "github.com/gin-gonic/gin"
  11. )
  12. type derivedPricingFunc func(req, resp map[string]any) (any, error)
  13. var derivedPricingFuncs = map[string]derivedPricingFunc{}
  14. type ResolvedDimensionValues struct {
  15. Values map[string]any
  16. Missing map[string]bool
  17. RawInput map[string]any
  18. }
  19. type DimensionResolver struct {
  20. c *gin.Context
  21. request map[string]any
  22. response map[string]any
  23. }
  24. func NewDimensionResolver(c *gin.Context) (*DimensionResolver, error) {
  25. contentType := c.Request.Header.Get("Content-Type")
  26. req := map[string]any{}
  27. switch {
  28. case strings.HasPrefix(contentType, "application/json"):
  29. storage, err := common.GetBodyStorage(c)
  30. if err != nil {
  31. return nil, err
  32. }
  33. body, err := storage.Bytes()
  34. if err != nil {
  35. return nil, err
  36. }
  37. if len(strings.TrimSpace(string(body))) > 0 {
  38. if err := common.Unmarshal(body, &req); err != nil {
  39. return nil, err
  40. }
  41. }
  42. if err := resetRequestBody(c, storage); err != nil {
  43. return nil, err
  44. }
  45. case strings.Contains(contentType, gin.MIMEMultipartPOSTForm):
  46. form, err := common.ParseMultipartFormReusable(c)
  47. if err != nil {
  48. return nil, err
  49. }
  50. req = requestMapFromMultipart(form)
  51. default:
  52. return nil, fmt.Errorf("unsupported content-type for pricing dimensions: %s", contentType)
  53. }
  54. return &DimensionResolver{
  55. c: c,
  56. request: req,
  57. response: map[string]any{},
  58. }, nil
  59. }
  60. func (r *DimensionResolver) ResolveDimensions(dims []types.PricingDimension) (*ResolvedDimensionValues, error) {
  61. resolved := &ResolvedDimensionValues{
  62. Values: make(map[string]any, len(dims)),
  63. Missing: make(map[string]bool),
  64. RawInput: types.CloneMapAny(r.request),
  65. }
  66. for _, dim := range dims {
  67. value, exists, err := r.resolveSource(dim.Source)
  68. if err != nil {
  69. return nil, err
  70. }
  71. if !exists || !hasNonEmptyValue(value) {
  72. if dim.Optional {
  73. resolved.Values[dim.Key] = dim.Default
  74. continue
  75. }
  76. resolved.Values[dim.Key] = nil
  77. resolved.Missing[dim.Key] = true
  78. continue
  79. }
  80. converted, err := convertPricingDimensionValue(value, dim.Type)
  81. if err != nil {
  82. return nil, fmt.Errorf("dimension %q: %w", dim.Key, err)
  83. }
  84. resolved.Values[dim.Key] = converted
  85. }
  86. return resolved, nil
  87. }
  88. func IsAllowedPricingSource(source string) bool {
  89. if strings.HasPrefix(source, "response.") {
  90. return false
  91. }
  92. if strings.HasPrefix(source, "request.") {
  93. return len(strings.TrimPrefix(source, "request.")) > 0
  94. }
  95. if _, ok := derivedPricingFuncs[strings.TrimPrefix(source, "derived.")]; ok && strings.HasPrefix(source, "derived.") {
  96. return true
  97. }
  98. return false
  99. }
  100. func registerDerivedPricingFunc(name string, fn derivedPricingFunc) {
  101. derivedPricingFuncs[name] = fn
  102. }
  103. func (r *DimensionResolver) resolveSource(source string) (any, bool, error) {
  104. if strings.HasPrefix(source, "response.") {
  105. return nil, false, fmt.Errorf("pricing source %q is unsupported in schema v1", source)
  106. }
  107. if strings.HasPrefix(source, "request.") {
  108. return getNestedValue(r.request, strings.TrimPrefix(source, "request."))
  109. }
  110. if strings.HasPrefix(source, "derived.") {
  111. name := strings.TrimPrefix(source, "derived.")
  112. fn, ok := derivedPricingFuncs[name]
  113. if !ok {
  114. return nil, false, fmt.Errorf("derived pricing source %q is not registered", source)
  115. }
  116. value, err := fn(r.request, r.response)
  117. if err != nil {
  118. return nil, false, err
  119. }
  120. return value, hasNonEmptyValue(value), nil
  121. }
  122. return nil, false, fmt.Errorf("pricing source %q is not allowed", source)
  123. }
  124. func getNestedValue(src map[string]any, path string) (any, bool, error) {
  125. if path == "" {
  126. return nil, false, nil
  127. }
  128. var cur any = src
  129. for _, part := range strings.Split(path, ".") {
  130. m, ok := cur.(map[string]any)
  131. if !ok {
  132. return nil, false, nil
  133. }
  134. cur, ok = m[part]
  135. if !ok {
  136. return nil, false, nil
  137. }
  138. }
  139. return cur, true, nil
  140. }
  141. func convertPricingDimensionValue(value any, typ string) (any, error) {
  142. switch typ {
  143. case "string":
  144. if s, ok := value.(string); ok {
  145. return s, nil
  146. }
  147. return fmt.Sprintf("%v", value), nil
  148. case "number":
  149. switch v := value.(type) {
  150. case float64:
  151. return v, nil
  152. case float32:
  153. return float64(v), nil
  154. case int:
  155. return float64(v), nil
  156. case int64:
  157. return float64(v), nil
  158. case int32:
  159. return float64(v), nil
  160. case string:
  161. f, err := strconv.ParseFloat(strings.TrimSpace(v), 64)
  162. if err != nil {
  163. return nil, fmt.Errorf("value %q is not a number", v)
  164. }
  165. return f, nil
  166. default:
  167. return nil, fmt.Errorf("value is not a number")
  168. }
  169. case "boolean":
  170. switch v := value.(type) {
  171. case bool:
  172. return v, nil
  173. case string:
  174. b, err := strconv.ParseBool(strings.TrimSpace(v))
  175. if err != nil {
  176. return nil, fmt.Errorf("value %q is not a boolean", v)
  177. }
  178. return b, nil
  179. default:
  180. return nil, fmt.Errorf("value is not a boolean")
  181. }
  182. default:
  183. return nil, fmt.Errorf("unknown dimension type %q", typ)
  184. }
  185. }
  186. func requestMapFromMultipart(form *multipart.Form) map[string]any {
  187. req := map[string]any{}
  188. for key, vals := range form.Value {
  189. if len(vals) == 1 {
  190. assignNestedValue(req, key, vals[0])
  191. continue
  192. }
  193. copied := append([]string(nil), vals...)
  194. assignNestedValue(req, key, copied)
  195. }
  196. for key, files := range form.File {
  197. if len(files) == 1 {
  198. assignNestedValue(req, key, files[0].Filename)
  199. continue
  200. }
  201. names := make([]string, 0, len(files))
  202. for _, file := range files {
  203. names = append(names, file.Filename)
  204. }
  205. assignNestedValue(req, key, names)
  206. }
  207. return req
  208. }
  209. func assignNestedValue(dst map[string]any, key string, value any) {
  210. parts := strings.Split(key, ".")
  211. cur := dst
  212. for i, part := range parts {
  213. if i == len(parts)-1 {
  214. cur[part] = value
  215. return
  216. }
  217. next, ok := cur[part].(map[string]any)
  218. if !ok {
  219. next = map[string]any{}
  220. cur[part] = next
  221. }
  222. cur = next
  223. }
  224. }
  225. func resetRequestBody(c *gin.Context, storage common.BodyStorage) error {
  226. if _, err := storage.Seek(0, io.SeekStart); err != nil {
  227. return err
  228. }
  229. c.Request.Body = io.NopCloser(storage)
  230. return nil
  231. }