added simulate stages. Changed GSN_PREDICTOR_GRIB_TTL to 48h

This commit is contained in:
afanasyev.aa 2025-12-09 18:25:16 +09:00
parent c4f355a32e
commit fe207f3fab
21 changed files with 978 additions and 137 deletions

View file

@ -5,14 +5,14 @@ package gsn
import (
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
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"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
)
var (
@ -32,6 +32,7 @@ type otelConfig struct {
Tracer trace.Tracer
MeterProvider metric.MeterProvider
Meter metric.Meter
Attributes []attribute.KeyValue
}
func (cfg *otelConfig) initOTEL() {
@ -215,6 +216,13 @@ func WithMeterProvider(provider metric.MeterProvider) Option {
})
}
// WithAttributes specifies default otel attributes.
func WithAttributes(attributes ...attribute.KeyValue) Option {
return otelOptionFunc(func(cfg *otelConfig) {
cfg.Attributes = attributes
})
}
// WithClient specifies http client to use.
func WithClient(client ht.Client) ClientOption {
return optionFunc[clientConfig](func(cfg *clientConfig) {

View file

@ -9,16 +9,15 @@ import (
"time"
"github.com/go-faster/errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"github.com/ogen-go/ogen/conv"
ht "github.com/ogen-go/ogen/http"
"github.com/ogen-go/ogen/otelogen"
"github.com/ogen-go/ogen/uri"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
)
func trimTrailingSlashes(u *url.URL) {
@ -103,8 +102,9 @@ func (c *Client) sendPerformPrediction(ctx context.Context, params PerformPredic
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("performPrediction"),
semconv.HTTPRequestMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/api/v1/prediction"),
semconv.URLTemplateKey.String("/api/v1/prediction"),
}
otelAttrs = append(otelAttrs, c.cfg.Attributes...)
// Run stopwatch.
startTime := time.Now()
@ -345,6 +345,32 @@ func (c *Client) sendPerformPrediction(ctx context.Context, params PerformPredic
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "simulate_stages" parameter.
cfg := uri.QueryParameterEncodingConfig{
Name: "simulate_stages",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if params.SimulateStages != nil {
return e.EncodeArray(func(e uri.Encoder) error {
for i, item := range params.SimulateStages {
if err := func() error {
return e.EncodeValue(conv.StringToString(string(item)))
}(); err != nil {
return errors.Wrapf(err, "[%d]", i)
}
}
return nil
})
}
return nil
}); err != nil {
return res, errors.Wrap(err, "encode query")
}
}
{
// Encode "interpolate" parameter.
cfg := uri.QueryParameterEncodingConfig{
@ -434,8 +460,9 @@ func (c *Client) sendReadinessCheck(ctx context.Context) (res *ReadinessResponse
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("readinessCheck"),
semconv.HTTPRequestMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/ready"),
semconv.URLTemplateKey.String("/ready"),
}
otelAttrs = append(otelAttrs, c.cfg.Attributes...)
// Run stopwatch.
startTime := time.Now()

View file

@ -8,16 +8,15 @@ import (
"time"
"github.com/go-faster/errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
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.37.0"
"go.opentelemetry.io/otel/trace"
)
type codeRecorder struct {
@ -30,6 +29,10 @@ func (c *codeRecorder) WriteHeader(status int) {
c.ResponseWriter.WriteHeader(status)
}
func (c *codeRecorder) Unwrap() http.ResponseWriter {
return c.ResponseWriter
}
// handlePerformPredictionRequest handles performPrediction operation.
//
// Perform prediction.
@ -86,7 +89,7 @@ func (s *Server) handlePerformPredictionRequest(args [0]string, argsEscaped bool
// 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 {
if code < 100 || code >= 500 {
span.SetStatus(codes.Error, stage)
}
@ -115,6 +118,8 @@ func (s *Server) handlePerformPredictionRequest(args [0]string, argsEscaped bool
return
}
var rawBody []byte
var response *PredictionResult
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
@ -123,6 +128,7 @@ func (s *Server) handlePerformPredictionRequest(args [0]string, argsEscaped bool
OperationSummary: "Perform prediction",
OperationID: "performPrediction",
Body: nil,
RawBody: rawBody,
Params: middleware.Parameters{
{
Name: "launch_latitude",
@ -172,6 +178,10 @@ func (s *Server) handlePerformPredictionRequest(args [0]string, argsEscaped bool
Name: "descent_curve",
In: "query",
}: params.DescentCurve,
{
Name: "simulate_stages",
In: "query",
}: params.SimulateStages,
{
Name: "interpolate",
In: "query",
@ -291,7 +301,7 @@ func (s *Server) handleReadinessCheckRequest(args [0]string, argsEscaped bool, w
// 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 {
if code < 100 || code >= 500 {
span.SetStatus(codes.Error, stage)
}
@ -306,6 +316,8 @@ func (s *Server) handleReadinessCheckRequest(args [0]string, argsEscaped bool, w
err error
)
var rawBody []byte
var response *ReadinessResponse
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
@ -314,6 +326,7 @@ func (s *Server) handleReadinessCheckRequest(args [0]string, argsEscaped bool, w
OperationSummary: "Readiness check",
OperationID: "readinessCheck",
Body: nil,
RawBody: rawBody,
Params: middleware.Parameters{},
Raw: r,
}

View file

@ -9,7 +9,6 @@ import (
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"github.com/ogen-go/ogen/json"
"github.com/ogen-go/ogen/validate"
)
@ -607,6 +606,8 @@ func (s *PredictionResultPredictionItemStage) Decode(d *jx.Decoder) error {
*s = PredictionResultPredictionItemStageAscent
case PredictionResultPredictionItemStageDescent:
*s = PredictionResultPredictionItemStageDescent
case PredictionResultPredictionItemStageFloat:
*s = PredictionResultPredictionItemStageFloat
default:
*s = PredictionResultPredictionItemStage(v)
}

View file

@ -3,11 +3,11 @@
package gsn
import (
"fmt"
"net/http"
"time"
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/conv"
"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
@ -17,21 +17,22 @@ import (
// PerformPredictionParams is parameters of performPrediction operation.
type PerformPredictionParams struct {
LaunchLatitude OptFloat64
LaunchLongitude OptFloat64
LaunchDatetime OptDateTime
LaunchAltitude OptFloat64
Profile OptPerformPredictionProfile
AscentRate OptFloat64
BurstAltitude OptFloat64
DescentRate OptFloat64
FloatAltitude OptFloat64
StopDatetime OptDateTime
AscentCurve OptString
DescentCurve OptString
Interpolate OptBool
Format OptPerformPredictionFormat
Dataset OptDateTime
LaunchLatitude OptFloat64 `json:",omitempty,omitzero"`
LaunchLongitude OptFloat64 `json:",omitempty,omitzero"`
LaunchDatetime OptDateTime `json:",omitempty,omitzero"`
LaunchAltitude OptFloat64 `json:",omitempty,omitzero"`
Profile OptPerformPredictionProfile `json:",omitempty,omitzero"`
AscentRate OptFloat64 `json:",omitempty,omitzero"`
BurstAltitude OptFloat64 `json:",omitempty,omitzero"`
DescentRate OptFloat64 `json:",omitempty,omitzero"`
FloatAltitude OptFloat64 `json:",omitempty,omitzero"`
StopDatetime OptDateTime `json:",omitempty,omitzero"`
AscentCurve OptString `json:",omitempty,omitzero"`
DescentCurve OptString `json:",omitempty,omitzero"`
SimulateStages []PerformPredictionSimulateStagesItem `json:",omitempty"`
Interpolate OptBool `json:",omitempty,omitzero"`
Format OptPerformPredictionFormat `json:",omitempty,omitzero"`
Dataset OptDateTime `json:",omitempty,omitzero"`
}
func unpackPerformPredictionParams(packed middleware.Parameters) (params PerformPredictionParams) {
@ -143,6 +144,15 @@ func unpackPerformPredictionParams(packed middleware.Parameters) (params Perform
params.DescentCurve = v.(OptString)
}
}
{
key := middleware.ParameterKey{
Name: "simulate_stages",
In: "query",
}
if v, ok := packed[key]; ok {
params.SimulateStages = v.([]PerformPredictionSimulateStagesItem)
}
}
{
key := middleware.ParameterKey{
Name: "interpolate",
@ -787,6 +797,71 @@ func decodePerformPredictionParams(args [0]string, argsEscaped bool, r *http.Req
Err: err,
}
}
// Decode query: simulate_stages.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{
Name: "simulate_stages",
Style: uri.QueryStyleForm,
Explode: true,
}
if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
return d.DecodeArray(func(d uri.Decoder) error {
var paramsDotSimulateStagesVal PerformPredictionSimulateStagesItem
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToString(val)
if err != nil {
return err
}
paramsDotSimulateStagesVal = PerformPredictionSimulateStagesItem(c)
return nil
}(); err != nil {
return err
}
params.SimulateStages = append(params.SimulateStages, paramsDotSimulateStagesVal)
return nil
})
}); err != nil {
return err
}
if err := func() error {
var failures []validate.FieldError
for i, elem := range params.SimulateStages {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "simulate_stages",
In: "query",
Err: err,
}
}
// Decode query: interpolate.
if err := func() error {
cfg := uri.QueryParameterDecodingConfig{

View file

@ -9,7 +9,6 @@ import (
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)

View file

@ -7,10 +7,9 @@ import (
"github.com/go-faster/errors"
"github.com/go-faster/jx"
ht "github.com/ogen-go/ogen/http"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
ht "github.com/ogen-go/ogen/http"
)
func encodePerformPredictionResponse(response *PredictionResult, w http.ResponseWriter, span trace.Span) error {

View file

@ -109,12 +109,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Route is route object.
type Route struct {
name string
summary string
operationID string
pathPattern string
count int
args [0]string
name string
summary string
operationID string
operationGroup string
pathPattern string
count int
args [0]string
}
// Name returns ogen operation name.
@ -134,6 +135,11 @@ func (r Route) OperationID() string {
return r.operationID
}
// OperationGroup returns the x-ogen-operation-group value.
func (r Route) OperationGroup() string {
return r.operationGroup
}
// PathPattern returns OpenAPI path.
func (r Route) PathPattern() string {
return r.pathPattern
@ -209,6 +215,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
r.name = PerformPredictionOperation
r.summary = "Perform prediction"
r.operationID = "performPrediction"
r.operationGroup = ""
r.pathPattern = "/api/v1/prediction"
r.args = args
r.count = 0
@ -233,6 +240,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
r.name = ReadinessCheckOperation
r.summary = "Readiness check"
r.operationID = "readinessCheck"
r.operationGroup = ""
r.pathPattern = "/ready"
r.args = args
r.count = 0

View file

@ -430,6 +430,54 @@ func (s *PerformPredictionProfile) UnmarshalText(data []byte) error {
}
}
type PerformPredictionSimulateStagesItem string
const (
PerformPredictionSimulateStagesItemAscent PerformPredictionSimulateStagesItem = "ascent"
PerformPredictionSimulateStagesItemDescent PerformPredictionSimulateStagesItem = "descent"
PerformPredictionSimulateStagesItemFloat PerformPredictionSimulateStagesItem = "float"
)
// AllValues returns all PerformPredictionSimulateStagesItem values.
func (PerformPredictionSimulateStagesItem) AllValues() []PerformPredictionSimulateStagesItem {
return []PerformPredictionSimulateStagesItem{
PerformPredictionSimulateStagesItemAscent,
PerformPredictionSimulateStagesItemDescent,
PerformPredictionSimulateStagesItemFloat,
}
}
// MarshalText implements encoding.TextMarshaler.
func (s PerformPredictionSimulateStagesItem) MarshalText() ([]byte, error) {
switch s {
case PerformPredictionSimulateStagesItemAscent:
return []byte(s), nil
case PerformPredictionSimulateStagesItemDescent:
return []byte(s), nil
case PerformPredictionSimulateStagesItemFloat:
return []byte(s), nil
default:
return nil, errors.Errorf("invalid value: %q", s)
}
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (s *PerformPredictionSimulateStagesItem) UnmarshalText(data []byte) error {
switch PerformPredictionSimulateStagesItem(data) {
case PerformPredictionSimulateStagesItemAscent:
*s = PerformPredictionSimulateStagesItemAscent
return nil
case PerformPredictionSimulateStagesItemDescent:
*s = PerformPredictionSimulateStagesItemDescent
return nil
case PerformPredictionSimulateStagesItemFloat:
*s = PerformPredictionSimulateStagesItemFloat
return nil
default:
return errors.Errorf("invalid value: %q", data)
}
}
// Ref: #/components/schemas/PredictionResult
type PredictionResult struct {
Metadata PredictionResultMetadata `json:"metadata"`
@ -511,6 +559,7 @@ type PredictionResultPredictionItemStage string
const (
PredictionResultPredictionItemStageAscent PredictionResultPredictionItemStage = "ascent"
PredictionResultPredictionItemStageDescent PredictionResultPredictionItemStage = "descent"
PredictionResultPredictionItemStageFloat PredictionResultPredictionItemStage = "float"
)
// AllValues returns all PredictionResultPredictionItemStage values.
@ -518,6 +567,7 @@ func (PredictionResultPredictionItemStage) AllValues() []PredictionResultPredict
return []PredictionResultPredictionItemStage{
PredictionResultPredictionItemStageAscent,
PredictionResultPredictionItemStageDescent,
PredictionResultPredictionItemStageFloat,
}
}
@ -528,6 +578,8 @@ func (s PredictionResultPredictionItemStage) MarshalText() ([]byte, error) {
return []byte(s), nil
case PredictionResultPredictionItemStageDescent:
return []byte(s), nil
case PredictionResultPredictionItemStageFloat:
return []byte(s), nil
default:
return nil, errors.Errorf("invalid value: %q", s)
}
@ -542,6 +594,9 @@ func (s *PredictionResultPredictionItemStage) UnmarshalText(data []byte) error {
case PredictionResultPredictionItemStageDescent:
*s = PredictionResultPredictionItemStageDescent
return nil
case PredictionResultPredictionItemStageFloat:
*s = PredictionResultPredictionItemStageFloat
return nil
default:
return errors.Errorf("invalid value: %q", data)
}

View file

@ -6,7 +6,6 @@ import (
"fmt"
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/validate"
)
@ -34,6 +33,19 @@ func (s PerformPredictionProfile) Validate() error {
}
}
func (s PerformPredictionSimulateStagesItem) Validate() error {
switch s {
case "ascent":
return nil
case "descent":
return nil
case "float":
return nil
default:
return errors.Errorf("invalid value: %v", s)
}
}
func (s *PredictionResult) Validate() error {
if s == nil {
return validate.ErrNilPointer
@ -131,6 +143,8 @@ func (s PredictionResultPredictionItemStage) Validate() error {
return nil
case "descent":
return nil
case "float":
return nil
default:
return errors.Errorf("invalid value: %v", s)
}