parse.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package web
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "io"
  6. "mime/multipart"
  7. "net/http"
  8. p "github.com/ajswis/go-pkparse-server/pokemon"
  9. pokemonparsing "github.com/ajswis/go-pkparse-server/pokemon-parsing"
  10. "github.com/gin-gonic/gin"
  11. )
  12. /*
  13. File uploads:
  14. curl -X POST http://localhost:8080/parse \
  15. -H "Content-Type: multipart/form-data" \
  16. --form "pokemon=@/path/to/test/file1" \
  17. --form "pokemon=@/path/to/test/file2"
  18. JSON POST:
  19. curl -X POST http://localhost:8080/parse \
  20. -H "Content-Type: application/json" \
  21. -d '{"pokemon": ["base64_encoded_pokemon_file", "another_base64_encoded_pokemon_file"]}'
  22. Form POST:
  23. curl -X POST http://localhost:8080/parse \
  24. -H "Content-Type: multipart/form-data" \
  25. --form 'pokemon=base64_encoded_pokemon_file' \
  26. --form 'pokemon=another_base64_encoded_pokemon_file'
  27. Form POST + File upload:
  28. curl -X POST http://localhost:8080/parse \
  29. -H "Content-Type: multipart/form-data" \
  30. --form "pokemon=@/path/to/test/file1" \
  31. --form "pokemon=@/path/to/test/file2" \
  32. --form 'pokemon=base64_encoded_pokemon_file' \
  33. */
  34. type pokemonParseRequest struct {
  35. Pokemon []string `json:"pokemon" form:"pokemon"`
  36. }
  37. // parse grabs multipart file uploads under the key `pokemon`, aggregates them,
  38. // and delegates work to the parser.
  39. func (s *Server) parse(c *gin.Context) {
  40. var rawPokemon []p.RawPokemon
  41. // If the POST is `multipart/form-data`, check for file uploads and read them
  42. if c.ContentType() == "multipart/form-data" {
  43. fileHeaders, err := uploadedPokemonFiles(c)
  44. if err != nil {
  45. render(c, http.StatusInternalServerError, gin.H{"error": err.Error()})
  46. return
  47. }
  48. if len(fileHeaders) > 0 {
  49. err = pokemonFromFileUpload(fileHeaders, &rawPokemon)
  50. if err != nil {
  51. render(c, http.StatusInternalServerError, gin.H{"error": err.Error()})
  52. return
  53. }
  54. }
  55. }
  56. // For non-file-upload form submissions or all other `Content-Type`s
  57. err := pokemonFromEncodedUpload(c, &rawPokemon)
  58. if err != nil {
  59. render(c, http.StatusInternalServerError, gin.H{"error": err.Error()})
  60. return
  61. }
  62. pkmn, err := pokemonparsing.ParseAll(rawPokemon)
  63. if err != nil {
  64. render(c, http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
  65. return
  66. }
  67. render(c, http.StatusOK, pkmn)
  68. }
  69. // uploadedPokemonFiles extracts any existing FileHeaders from file uploads
  70. func uploadedPokemonFiles(c *gin.Context) ([]*multipart.FileHeader, error) {
  71. form, err := c.MultipartForm()
  72. if err != nil {
  73. return nil, err
  74. }
  75. return form.File["pokemon"], nil
  76. }
  77. // pokemonFromFileUpload reads data from uploaded files and returns the raw
  78. // pokemon
  79. func pokemonFromFileUpload(files []*multipart.FileHeader, rawPokemon *[]p.RawPokemon) error {
  80. for _, file := range files {
  81. var err error
  82. var src multipart.File
  83. src, err = file.Open()
  84. if err != nil {
  85. return err
  86. }
  87. defer src.Close()
  88. buf := bytes.NewBuffer(nil)
  89. if _, err = io.Copy(buf, src); err != nil {
  90. return err
  91. }
  92. *rawPokemon = append(*rawPokemon, buf.Bytes())
  93. }
  94. return nil
  95. }
  96. // pokemonFromEncodedUpload binds the POST data to a `pokemonParseRequest` and
  97. // converts each base64 string to a `[]byte` slice.
  98. func pokemonFromEncodedUpload(c *gin.Context, rawPokemon *[]p.RawPokemon) error {
  99. var request pokemonParseRequest
  100. if err := c.ShouldBind(&request); err != nil {
  101. return err
  102. }
  103. for _, pkmn := range request.Pokemon {
  104. var err error
  105. var decoded []byte
  106. decoded, err = base64.StdEncoding.DecodeString(pkmn)
  107. if err != nil {
  108. return err
  109. }
  110. *rawPokemon = append(*rawPokemon, decoded)
  111. }
  112. return nil
  113. }