// Code generated by ogen, DO NOT EDIT. package rest import ( "context" "net/http" "time" "github.com/go-faster/errors" ht "github.com/ogen-go/ogen/http" "github.com/ogen-go/ogen/middleware" "github.com/ogen-go/ogen/ogenerrors" "github.com/ogen-go/ogen/otelogen" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" ) type codeRecorder struct { http.ResponseWriter status int } func (c *codeRecorder) WriteHeader(status int) { c.status = status c.ResponseWriter.WriteHeader(status) } func (c *codeRecorder) Unwrap() http.ResponseWriter { return c.ResponseWriter } // handleCancelDatasetJobRequest handles cancelDatasetJob operation. // // Cancel a running download job. // // DELETE /api/v1/admin/jobs/{id} func (s *Server) handleCancelDatasetJobRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("cancelDatasetJob"), semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/api/v1/admin/jobs/{id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CancelDatasetJobOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: CancelDatasetJobOperation, ID: "cancelDatasetJob", } ) params, err := decodeCancelDatasetJobParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *CancelDatasetJobNoContent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: CancelDatasetJobOperation, OperationSummary: "Cancel a running download job", OperationID: "cancelDatasetJob", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "id", In: "path", }: params.ID, }, Raw: r, } type ( Request = struct{} Params = CancelDatasetJobParams Response = *CancelDatasetJobNoContent ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackCancelDatasetJobParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { err = s.h.CancelDatasetJob(ctx, params) return response, err }, ) } else { err = s.h.CancelDatasetJob(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeCancelDatasetJobResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleCancelPredictionJobRequest handles cancelPredictionJob operation. // // Cancel a queued prediction job. // // DELETE /api/v1/predictions/{id} func (s *Server) handleCancelPredictionJobRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("cancelPredictionJob"), semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/api/v1/predictions/{id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CancelPredictionJobOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: CancelPredictionJobOperation, ID: "cancelPredictionJob", } ) params, err := decodeCancelPredictionJobParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *CancelPredictionJobNoContent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: CancelPredictionJobOperation, OperationSummary: "Cancel a queued prediction job", OperationID: "cancelPredictionJob", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "id", In: "path", }: params.ID, }, Raw: r, } type ( Request = struct{} Params = CancelPredictionJobParams Response = *CancelPredictionJobNoContent ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackCancelPredictionJobParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { err = s.h.CancelPredictionJob(ctx, params) return response, err }, ) } else { err = s.h.CancelPredictionJob(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeCancelPredictionJobResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleCreatePredictionJobRequest handles createPredictionJob operation. // // Enqueue an asynchronous prediction. // // POST /api/v1/predictions func (s *Server) handleCreatePredictionJobRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("createPredictionJob"), semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/api/v1/predictions"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), CreatePredictionJobOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: CreatePredictionJobOperation, ID: "createPredictionJob", } ) var rawBody []byte request, rawBody, close, err := s.decodeCreatePredictionJobRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response *PredictionJob if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: CreatePredictionJobOperation, OperationSummary: "Enqueue an asynchronous prediction", OperationID: "createPredictionJob", Body: request, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = *PredictionV2Request Params = struct{} Response = *PredictionJob ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.CreatePredictionJob(ctx, request) return response, err }, ) } else { response, err = s.h.CreatePredictionJob(ctx, request) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeCreatePredictionJobResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleDeleteDatasetRequest handles deleteDataset operation. // // Delete a stored dataset by filename. // // DELETE /api/v1/admin/datasets/{name} func (s *Server) handleDeleteDatasetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("deleteDataset"), semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/api/v1/admin/datasets/{name}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DeleteDatasetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: DeleteDatasetOperation, ID: "deleteDataset", } ) params, err := decodeDeleteDatasetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *DeleteDatasetNoContent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: DeleteDatasetOperation, OperationSummary: "Delete a stored dataset by filename", OperationID: "deleteDataset", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "name", In: "path", }: params.Name, }, Raw: r, } type ( Request = struct{} Params = DeleteDatasetParams Response = *DeleteDatasetNoContent ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackDeleteDatasetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { err = s.h.DeleteDataset(ctx, params) return response, err }, ) } else { err = s.h.DeleteDataset(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeDeleteDatasetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleGetDatasetJobRequest handles getDatasetJob operation. // // Get a dataset download job. // // GET /api/v1/admin/jobs/{id} func (s *Server) handleGetDatasetJobRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getDatasetJob"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/admin/jobs/{id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetDatasetJobOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: GetDatasetJobOperation, ID: "getDatasetJob", } ) params, err := decodeGetDatasetJobParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *DownloadJob if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: GetDatasetJobOperation, OperationSummary: "Get a dataset download job", OperationID: "getDatasetJob", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "id", In: "path", }: params.ID, }, Raw: r, } type ( Request = struct{} Params = GetDatasetJobParams Response = *DownloadJob ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackGetDatasetJobParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.GetDatasetJob(ctx, params) return response, err }, ) } else { response, err = s.h.GetDatasetJob(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeGetDatasetJobResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleGetPredictionJobRequest handles getPredictionJob operation. // // Poll an asynchronous prediction job. // // GET /api/v1/predictions/{id} func (s *Server) handleGetPredictionJobRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getPredictionJob"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/predictions/{id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetPredictionJobOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: GetPredictionJobOperation, ID: "getPredictionJob", } ) params, err := decodeGetPredictionJobParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *PredictionJob if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: GetPredictionJobOperation, OperationSummary: "Poll an asynchronous prediction job", OperationID: "getPredictionJob", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "id", In: "path", }: params.ID, }, Raw: r, } type ( Request = struct{} Params = GetPredictionJobParams Response = *PredictionJob ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackGetPredictionJobParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.GetPredictionJob(ctx, params) return response, err }, ) } else { response, err = s.h.GetPredictionJob(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeGetPredictionJobResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleGetServiceStatusRequest handles getServiceStatus operation. // // Service status summary. // // GET /api/v1/admin/status func (s *Server) handleGetServiceStatusRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getServiceStatus"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/admin/status"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetServiceStatusOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) var rawBody []byte var response *StatusResponse if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: GetServiceStatusOperation, OperationSummary: "Service status summary", OperationID: "getServiceStatus", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = *StatusResponse ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.GetServiceStatus(ctx) return response, err }, ) } else { response, err = s.h.GetServiceStatus(ctx) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeGetServiceStatusResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleGetWindFieldRequest handles getWindField operation. // // Wind-field velocity grid (leaflet-velocity / wind-layer format). // // GET /api/v1/wind/field func (s *Server) handleGetWindFieldRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getWindField"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/wind/field"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetWindFieldOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: GetWindFieldOperation, ID: "getWindField", } ) params, err := decodeGetWindFieldParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response []WindComponent if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: GetWindFieldOperation, OperationSummary: "Wind-field velocity grid (leaflet-velocity / wind-layer format)", OperationID: "getWindField", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "time", In: "query", }: params.Time, { Name: "altitude", In: "query", }: params.Altitude, { Name: "min_lat", In: "query", }: params.MinLat, { Name: "max_lat", In: "query", }: params.MaxLat, { Name: "min_lng", In: "query", }: params.MinLng, { Name: "max_lng", In: "query", }: params.MaxLng, { Name: "step", In: "query", }: params.Step, }, Raw: r, } type ( Request = struct{} Params = GetWindFieldParams Response = []WindComponent ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackGetWindFieldParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.GetWindField(ctx, params) return response, err }, ) } else { response, err = s.h.GetWindField(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeGetWindFieldResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleGetWindMetaRequest handles getWindMeta operation. // // Wind-field visualization metadata. // // GET /api/v1/wind/meta func (s *Server) handleGetWindMetaRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getWindMeta"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/wind/meta"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), GetWindMetaOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) var rawBody []byte var response *WindMeta if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: GetWindMetaOperation, OperationSummary: "Wind-field visualization metadata", OperationID: "getWindMeta", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = *WindMeta ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.GetWindMeta(ctx) return response, err }, ) } else { response, err = s.h.GetWindMeta(ctx) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeGetWindMetaResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleListDatasetJobsRequest handles listDatasetJobs operation. // // List dataset download jobs. // // GET /api/v1/admin/jobs func (s *Server) handleListDatasetJobsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("listDatasetJobs"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/admin/jobs"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListDatasetJobsOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) var rawBody []byte var response []DownloadJob if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ListDatasetJobsOperation, OperationSummary: "List dataset download jobs", OperationID: "listDatasetJobs", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = []DownloadJob ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ListDatasetJobs(ctx) return response, err }, ) } else { response, err = s.h.ListDatasetJobs(ctx) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeListDatasetJobsResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleListDatasetsRequest handles listDatasets operation. // // List stored datasets. // // GET /api/v1/admin/datasets func (s *Server) handleListDatasetsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("listDatasets"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/admin/datasets"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ListDatasetsOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) var rawBody []byte var response *DatasetList if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ListDatasetsOperation, OperationSummary: "List stored datasets", OperationID: "listDatasets", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = *DatasetList ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ListDatasets(ctx) return response, err }, ) } else { response, err = s.h.ListDatasets(ctx) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeListDatasetsResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePerformPredictionRequest handles performPrediction operation. // // Tawhiri-compatible prediction. // // GET /api/v1/prediction func (s *Server) handlePerformPredictionRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("performPrediction"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/api/v1/prediction"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PerformPredictionOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PerformPredictionOperation, ID: "performPrediction", } ) params, err := decodePerformPredictionParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response *PredictionResponse if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PerformPredictionOperation, OperationSummary: "Tawhiri-compatible prediction", OperationID: "performPrediction", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "launch_latitude", In: "query", }: params.LaunchLatitude, { Name: "launch_longitude", In: "query", }: params.LaunchLongitude, { Name: "launch_datetime", In: "query", }: params.LaunchDatetime, { Name: "launch_altitude", In: "query", }: params.LaunchAltitude, { Name: "profile", In: "query", }: params.Profile, { Name: "ascent_rate", In: "query", }: params.AscentRate, { Name: "burst_altitude", In: "query", }: params.BurstAltitude, { Name: "descent_rate", In: "query", }: params.DescentRate, { Name: "float_altitude", In: "query", }: params.FloatAltitude, { Name: "stop_datetime", In: "query", }: params.StopDatetime, { Name: "dataset", In: "query", }: params.Dataset, }, Raw: r, } type ( Request = struct{} Params = PerformPredictionParams Response = *PredictionResponse ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPerformPredictionParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PerformPrediction(ctx, params) return response, err }, ) } else { response, err = s.h.PerformPrediction(ctx, params) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodePerformPredictionResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePerformPredictionV2Request handles performPredictionV2 operation. // // Profile-driven prediction (synchronous). // // POST /api/v2/prediction func (s *Server) handlePerformPredictionV2Request(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("performPredictionV2"), semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/api/v2/prediction"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PerformPredictionV2Operation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PerformPredictionV2Operation, ID: "performPredictionV2", } ) var rawBody []byte request, rawBody, close, err := s.decodePerformPredictionV2Request(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response *PredictionV2Response if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PerformPredictionV2Operation, OperationSummary: "Profile-driven prediction (synchronous)", OperationID: "performPredictionV2", Body: request, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = *PredictionV2Request Params = struct{} Response = *PredictionV2Response ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PerformPredictionV2(ctx, request) return response, err }, ) } else { response, err = s.h.PerformPredictionV2(ctx, request) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodePerformPredictionV2Response(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleReadinessCheckRequest handles readinessCheck operation. // // Readiness check. // // GET /ready func (s *Server) handleReadinessCheckRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("readinessCheck"), semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/ready"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ReadinessCheckOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) var rawBody []byte var response *ReadinessResponse if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ReadinessCheckOperation, OperationSummary: "Readiness check", OperationID: "readinessCheck", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = *ReadinessResponse ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ReadinessCheck(ctx) return response, err }, ) } else { response, err = s.h.ReadinessCheck(ctx) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeReadinessCheckResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleTriggerDatasetDownloadRequest handles triggerDatasetDownload operation. // // Trigger a dataset download. // // POST /api/v1/admin/datasets func (s *Server) handleTriggerDatasetDownloadRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("triggerDatasetDownload"), semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/api/v1/admin/datasets"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), TriggerDatasetDownloadOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: TriggerDatasetDownloadOperation, ID: "triggerDatasetDownload", } ) var rawBody []byte request, rawBody, close, err := s.decodeTriggerDatasetDownloadRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response *DownloadAccepted if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: TriggerDatasetDownloadOperation, OperationSummary: "Trigger a dataset download", OperationID: "triggerDatasetDownload", Body: request, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = *DownloadRequest Params = struct{} Response = *DownloadAccepted ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.TriggerDatasetDownload(ctx, request) return response, err }, ) } else { response, err = s.h.TriggerDatasetDownload(ctx, request) } if err != nil { if errRes, ok := errors.Into[*DefaultErrorStatusCode](err); ok { if err := encodeErrorResponse(errRes, w, span); err != nil { defer recordError("Internal", err) } return } if errors.Is(err, ht.ErrNotImplemented) { s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { defer recordError("Internal", err) } return } if err := encodeTriggerDatasetDownloadResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } }