Register an operation handler for an API. The handler must be a function that takes a context and a pointer to the input struct and returns a pointer to the output struct and an error. The input struct must be a struct with fields for the request path/query/header/cookie parameters and/or body. The
(api API, op Operation, handler func(context.Context, *I) (*O, error))
| 709 | // return resp, nil |
| 710 | // }) |
| 711 | func Register[I, O any](api API, op Operation, handler func(context.Context, *I) (*O, error)) { |
| 712 | oapi := api.OpenAPI() |
| 713 | registry := oapi.Components.Schemas |
| 714 | |
| 715 | if op.Method == "" { |
| 716 | panic("method must be specified in operation") |
| 717 | } |
| 718 | if op.Path == "" { |
| 719 | if grp, ok := api.(*Group); !ok || len(grp.prefixes) == 0 { |
| 720 | panic("path must be specified in operation") |
| 721 | } |
| 722 | } |
| 723 | initResponses(&op) |
| 724 | |
| 725 | inputType := reflect.TypeFor[I]() |
| 726 | if inputType.Kind() != reflect.Struct { |
| 727 | panic("input must be a struct") |
| 728 | } |
| 729 | inputParams, inputBodyIndex, hasInputBody, rawBodyIndex, rbt, rawBodyDataT, inSchema := processInputType(inputType, &op, registry) |
| 730 | |
| 731 | outputType := reflect.TypeFor[O]() |
| 732 | if outputType.Kind() != reflect.Struct { |
| 733 | panic("output must be a struct") |
| 734 | } |
| 735 | outHeaders, outStatusIndex, outBodyIndex, outBodyFunc := processOutputType(outputType, &op, registry) |
| 736 | |
| 737 | if len(op.Errors) > 0 { |
| 738 | if len(inputParams.Paths) > 0 || hasInputBody { |
| 739 | op.Errors = append(op.Errors, http.StatusUnprocessableEntity) |
| 740 | } |
| 741 | op.Errors = append(op.Errors, http.StatusInternalServerError) |
| 742 | } |
| 743 | defineErrors(&op, registry) |
| 744 | |
| 745 | if documenter, ok := api.(OperationDocumenter); ok { |
| 746 | // Enables customization of OpenAPI documentation behavior for operations. |
| 747 | documenter.DocumentOperation(&op) |
| 748 | } else if !op.Hidden { |
| 749 | oapi.AddOperation(&op) |
| 750 | } |
| 751 | |
| 752 | resolvers := findResolvers(resolverType, inputType) |
| 753 | defaults := findDefaults(registry, inputType) |
| 754 | |
| 755 | // Pre-compute query parameter validation data to reduce per-request allocations. |
| 756 | knownParams := make(map[string]struct{}) |
| 757 | var deepPrefixes []string |
| 758 | for i := range inputParams.Paths { |
| 759 | p := inputParams.Paths[i].Value |
| 760 | if p == nil || p.Loc != "query" { |
| 761 | continue |
| 762 | } |
| 763 | |
| 764 | if p.Style == styleDeepObject { |
| 765 | deepPrefixes = append(deepPrefixes, p.Name+"[") |
| 766 | continue |
| 767 | } |
| 768 |
no test coverage detected
searching dependent graphs…