Кодогенерация очень широко используется в го и надо уметь пользоваться этим инструментом.
В этом задании вам необходимо будет написать кодогенератор, который ищет методы структуры, помеченные спец меткой и генерирует для них следующий код:
- http-обёртки для этих методов
- проверку авторизации
- проверки метода (GET/POST)
- валидацию параметров
- заполнение структуры с параметрами метода
- обработку неизвестных ошибок
Т.е. вы пишите программу (в файлеhandlers_gen/codegen.go
) потом запускаете её, передавая в качестве параметров путь до файла для которого надо сгенерировать код, и путь до файла, в который записать результат. Запуск будет выглядеть примерно так: go build handlers_gen/* && ./codegen api.go api_handlers.go
. Т.е. запускаться он будет как бинарник_кодогенератора что_парсим.го куда_парсим.го
Хардкодить не надо. Все данные - имена полей, доступные значения, граничные значения - всё брать из самой струкруты, struct tags apivalidator
и кода, который мы парсим.
Если вы руками вписываете имя структуры, которое должно попасть в результирующий код после генерации - значит вы делаете не правильно, даже если у вас проходят тесты. Ваш кодогенератор должен работать универсально для любых полей и значений из тех что ему известны. Писать код надо так, чтобы он отработал на неизвестном вам коде, аналогичном api.go.
Единственное чем можно пользоваться - type ApiError struct
при проверке ошибки. Cчитаем что это какая-то общеизвестная структура.
Кодогенератор уммет обрабатывать следующие типы полей структуры:
int
string
Нам доступны следующие метки валидатора-заполнятора apivalidator
:
required
- поле не должно быть пустым (не должно иметь значение по-умолчанию)paramname
- если указано - то брать из параметра с этим именем, иначеlowercase
от имениenum
- "одно из"default
- если указано и приходит пустое значение (значение по-умолчанию) - устанавливать то что написано указано вdefault
min
- >= X для типаint
, для строкlen(str)
>=max
- <= X для типаint
Формат ошибок смотрите в тестах. Порядок следования ошибок:
- наличие метода (в
ServeHTTP
) - метод (POST)
- авторизация
- параметры в порядке следования в структуре
Авторизация проверяется просто на то что в хедере пришло значение 100500
Сгенерённый код будет иметь примерно такую цепочку
ServeHTTP
- принимает все методы из мультиплексора, если нашлось - вызывает handler$methodName
, если нет - говорит 404
handler$methodName
- обёртка над методом структуры $methodName
- осуществляет все проверки, выводит ошибки или результат в формате JSON
$methodName
- непосредственно метод структуры для которого мы генерируем код и, который парсим. Имеет префикс apigen:api
за которым следует json
с именем метода, типом и требованием авторизации. Его генерировать не нужно, он уже есть.
type SomeStructName struct{}
func (h *SomeStructName ) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "...":
h.wrapperDoSomeJob(w, r)
default:
// 404
}
}
func (h *SomeStructName ) wrapperDoSomeJob() {
// заполнение структуры params
// валидирование параметров
res, err := h.DoSomeJob(ctx, params)
// прочие обработки
}
По структуре кодогенератора - надо найти все методы, для каждого метода сгенерировать валидацию входящих параметров и прочие проверки в handler$methodName
, для пачки методов структуры сгенерировать обвязку в ServeHTTP
Над ошибками кодогенератора можно сильно не заморачиваться - параметры который в него передаются будем считать гарантированно корректными.
Что надо парсит в ast:
node.Decls
->ast.FuncDecl
- это метод. у него надо проверить что есть метка и начать генерировать для него обёрткуnode.Decls
->ast.GenDecl
->spec.(*ast.TypeSpec)
+currType.Type.(*ast.StructType)
- это структура. она нужна чтобы по ней генерить валидацию для метода, который мы нашли в проедыдущем пункте- https://golang.org/pkg/go/ast/#FuncDecl - тут смотрите к какой структуре относится метод
Советы:
- Вы можете использовать как шаблоны чтобы сгенерировать сразу весь метод, так и собирать код из маленьких кусков.
- Проще всего реализовать в 2 прохода - за первый собрать всё что надо сгенерить, за второй - собственно генерация кода
- Надо будет много конвертировать из интерфейсов, смотрите всегода что там лежит
fmt.Printf("type: %T data: %+v\n", val, val)
Структура директории:
- example/ - пример с кодогенерацией из 3-й лекции 1-й части курса. Можно этот код взять за основу.
- handlers_gen/codegen.go - сюда вам писать код
- api.go - этот файл вам надо скармливать в кодогенератор. редактировать его не надо
- main.go - тут всё ясно. редактировать не надо
- main_test.go - этот файл надо запускать для тестирования после кодогенерации. редактировать не надо
Запуск тестов будет происходить так:
# находясь в этой папке
# расширение .exe только для счастливых обладателей windows
# собирает кодогенератор и сразу же запускает генерацию http-хендлеров для файла api.go, записывая результат в api_handlers.go
go build handlers_gen/* && ./codegen.exe api.go api_handlers.go
# запуск тестов
go test -v