ggicci/httpin

implementation regexp for tag

Closed this issue ยท 5 comments

this package is absolutely awesome, i have used in my project.

i have some suggestion. static tag is awesome give consistency from developer.
but some cases, i need to create custom decoder to add values with regex key my structs.

this my personal opinion, using input with tag regex pattern key thats make more flexibility

// Example Struct
type SomeStruct struct {
 Search []string `in:re_query=^search_by_(\w+)_op_(\w+)$`
 Sort []string `in:re_query=^sort_by_(\w+)$`
}

thanks

ggicci commented

Hi, @zufzuf Thanks for using this package and giving feedbacks! Could you give a more comprehensive example as you mentioned above? I'd like to know:

  • the input: should be an HTTP request
  • the output: the instance of the struct with populated data

Hi @ggicci, thank u for response my issue.
this example for the input and output.
i hope this can make you understand what i mean.

my current code:
my decoder code

// You can edit this code!
// Click here and start typing.
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"regexp"

	"github.com/ggicci/httpin"
	"github.com/go-chi/chi/v5"
)

type Input struct {
	// can be single struct
	// Search Search `in:re_query=^search_by_(\w+)_op_(\w+)$`
	Search []Search `in:re_query=^search_by_(\w+)_op_(\w+)$`
	
	// can be single struct
	// Sort   Sort   `in:re_query=^sort_by_(\w+)$`
	Sort []Sort `in:re_query=^sort_by_(\w+)$`
}

// propose to support 
// `httpin.ReMatch`
type HttpinReMatch struct {
	RE *regexp.Regexp
	Match  string
}

type Search struct {
	// using match `key` and `value`
	Key   string   `in:"re_match=key"`
	Value []string `in:"re_match=value"`
}

// OR Using `httpin.ReMatch` struct to parse key to `Regexp` struct
// type Search struct {
//	httpin.ReMatch
//	Value []string `in:"re_match=value"`
// }

type Sort struct {
	Key   string `in:"re_match=key"`
	Value string `in:"re_match=value"`
}

func FindHandler(rw http.ResponseWriter, r *http.Request) {
	in := r.Context().Value(httpin.Input).(*Input)
	for _, search := range in.Search {
		// search.Key
		// search.Value
		//
		// OR
		//
		// matches := search.RE.FindStringSubmatch(search.Match)
		// matches[1] -> name, division
		// matches[2] -> is, is_not
	}
	
	for _, sort := range in.Sort {
		// search.Key
		// search.Value
	}
	
	fmt.Printf("input: %#v\n", input)
}

func main() {
	router := chi.NewRouter()
	router.With(
		httpin.NewInput(Input{}),
	).Get("/inputs", FindHandler)

	r, _ := http.NewRequest("GET", "/input?search_by_name_op_is=Lorem&sort_by_name=DESC&search_by_name_op_is=Ipsum&search_by_division_op_is_not=Dolor", nil)

	rw := httptest.NewRecorder()
	router.ServeHTTP(rw, r)
}
ggicci commented

This use case actually is too complex and quite specialized. And obviously it's out of httpin's scope of work.

I'm not sure if you are designing your API from scratch or just dealing with existing systems.

If you are desinging your API. Please consider: IMO to streamline, I would recommend just spliting them to multiple query parameters and finally zipping them together correspondingly. Here is my solution (or may be an optional workaround to you):

type Input struct {
	SearchBy  []string `in:"query=search_by"`  // [ "name", "name", "division" ]
	SearchOp  []string `in:"query=search_op"`  // [ "is", "is", "is_not" ]
	SearchKey []string `in:"query=search_key"` // [ "Lorem", "Ipsum", "Dolor" ]
	SortBy    []string `in:"query=sort_by"`    // [ "name" ]
	SortOp    []string `in:"query=sort_op"`    // [ "DESC" ]
}

On the server side, zip them together to get tuples or structs like the following:

// Search:
[
    { "name", "is", "Lorem" },
    { "name", "is", "Ipsum" },
    { "division", "is_not", "Dolor" },
]

To get it back to the point, if you are dealing with existing systems, and currently the service has already been brought online. Please let me know. BTW, I'm not planning to support this as a built-in function. As I said above, it's too complex. Because it brings both dynamic keys and values to the query parameters. But if it's the case, I'm happy to give some suggestions on how to implement it by yourself by customizing httpin.

hi @ggicci, thanks for response.
the problem is when i make like your struct on front-end server thats query can messed up and not in correct order, making impossible to handle.
i hope like tag can support wildcard or regex.
i have thinking, i will use search_by and make flexibility on value like ?search_by=is_Lorem. i think more possible with httpin.
i will try using customizing httpin, you can close this issue after read this.
thank u for suggestion. have a good day.

ggicci commented

the problem is when i make like your struct on front-end server thats query can messed up and not in correct order, making impossible to handle.

Hi @zufzuf, httpin does respect the order of the parameters in a query, please have a look at this example: https://go.dev/play/p/Fs8GptE41-c

Thank you, too :)

I'm glad to help!