Skip to content

Commit

Permalink
moved otel engine to internal
Browse files Browse the repository at this point in the history
  • Loading branch information
kofoworola committed Jul 25, 2023
1 parent 27e7ed2 commit a1d286a
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 30 deletions.
16 changes: 7 additions & 9 deletions ci/tests/tracing/scenarios/tyk_test-graphql-tracing_200.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
type: Test
spec:
id: hXtQIVq4R
id: smwk-S3VR
name: Test Graphql Tracing
description: Test Graphql Tracing
description: Test Graphql Tracing And Proper Spans
trigger:
type: http
httpRequest:
method: GET
url: tyk:8080/test-graphql-tracing/test-grapql-tracing
method: POST
url: tyk:8080/test-graphql-tracing/test-graphql-tracing
body: "{\n \"query\": \"{\\n country(code: \\\"NG\\\"){\\n name\\n }\\n}\"\n}"
headers:
- key: Content-Type
value: application/json
specs:
- selector: span[tracetest.span.type="general" name="GraphqlEngine"]
name: Check if the graphql engine span exists
assertions:
- attr:name = "GraphqlEngine"
- selector: span[tracetest.span.type="http" name="HTTP POST" http.method="POST"]
name: Upstream Request is valid
assertions:
- attr:http.status_code = 200
- attr:http.status_code = 200
- attr:http.url = "https://countries.trevorblades.com/"
11 changes: 6 additions & 5 deletions gateway/api_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
graphqlinternal "github.com/TykTechnologies/tyk/internal/graphql"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -216,11 +217,11 @@ type APISpec struct {
network analytics.NetworkStats

GraphQLExecutor struct {
Engine *graphql.ExecutionEngine
CancelV2 context.CancelFunc
EngineV2 *graphql.ExecutionEngineV2
CustomExecutor graphql.ExecutionEngineV2Executor
HooksV2 struct {
Engine *graphql.ExecutionEngine
CancelV2 context.CancelFunc
EngineV2 *graphql.ExecutionEngineV2
OtelExecutor *graphqlinternal.OtelGraphqlEngineV2
HooksV2 struct {
BeforeFetchHook resolve.BeforeFetchHook
AfterFetchHook resolve.AfterFetchHook
}
Expand Down
52 changes: 49 additions & 3 deletions gateway/mw_graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"github.com/TykTechnologies/tyk/internal/graphql"
"net/http"

"github.com/gorilla/websocket"
Expand Down Expand Up @@ -187,14 +188,15 @@ func (m *GraphQLMiddleware) initGraphQLEngineV2(logger *abstractlogger.LogrusLog
}
m.Spec.GraphQLExecutor.EngineV2 = engine
conf := m.Gw.GetConfig()
// TODO check if the root executor can be set without using a getter
if conf.OpenTelemetry.Enabled {
executor := NewOtelGraphqlEngineV2(m.Gw.TracerProvider, engine)
executor := graphql.NewOtelGraphqlEngineV2(m.Gw.TracerProvider, engine)
rootExecutor, err := gql.NewCustomExecutionEngineV2Executor(executor)
if err != nil {
m.Logger().WithError(err).Error("error creating custom execution engine v2")
}
executor.setRootExecutor(rootExecutor)
m.Spec.GraphQLExecutor.CustomExecutor = executor
executor.SetRootExecutor(rootExecutor)
m.Spec.GraphQLExecutor.OtelExecutor = executor
} else {

}
Expand Down Expand Up @@ -244,7 +246,14 @@ func (m *GraphQLMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Reques
}

defer ctxSetGraphQLRequest(r, &gqlRequest)
if conf := m.Gw.GetConfig(); conf.OpenTelemetry.Enabled {
return m.validateRequestWithOtel(r.Context(), w, &gqlRequest)
} else {
return m.validateRequest(w, &gqlRequest)
}
}

func (m *GraphQLMiddleware) validateRequest(w http.ResponseWriter, gqlRequest *gql.Request) (error, int) {
normalizationResult, err := gqlRequest.Normalize(m.Spec.GraphQLExecutor.Schema)
if err != nil {
m.Logger().Errorf("Error while normalizing GraphQL request: '%s'", err)
Expand Down Expand Up @@ -273,7 +282,44 @@ func (m *GraphQLMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Reques
if inputValidationResult.Errors != nil && inputValidationResult.Errors.Count() > 0 {
return m.writeGraphQLError(w, inputValidationResult.Errors)
}
return nil, http.StatusOK
}

func (m *GraphQLMiddleware) validateRequestWithOtel(ctx context.Context, w http.ResponseWriter, req *gql.Request) (error, int) {
m.Spec.GraphQLExecutor.OtelExecutor.SetContext(ctx)

// normalization
err := m.Spec.GraphQLExecutor.OtelExecutor.Normalize(req)
if err != nil {
m.Logger().Errorf("Error while normalizing GraphqlRequest: %v", err)
if err, ok := err.(*gql.RequestErrors); ok {
return m.writeGraphQLError(w, err)
} else {
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
}

// validation
err = m.Spec.GraphQLExecutor.OtelExecutor.ValidateForSchema(req)
if err != nil {
m.Logger().Errorf("Error while validating GraphQL request: '%s'", err)
if err, ok := err.(*gql.RequestErrors); ok {
return m.writeGraphQLError(w, err)
} else {
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
}

// input validation
err = m.Spec.GraphQLExecutor.OtelExecutor.InputValidation(req)
if err != nil {
m.Logger().Errorf("Error while validating variables for request: %v", err)
if err, ok := err.(*gql.RequestErrors); ok {
return m.writeGraphQLError(w, err)
} else {
return ProxyingRequestFailedErr, http.StatusInternalServerError
}
}
return nil, http.StatusOK
}

Expand Down
4 changes: 2 additions & 2 deletions gateway/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -1168,8 +1168,8 @@ func (p *ReverseProxy) handoverRequestToGraphQLExecutionEngine(roundTripper *Tyk
}
execOptions = append(execOptions, graphql.WithUpstreamHeaders(upstreamHeaders))

if p.TykAPISpec.GraphQLExecutor.CustomExecutor != nil {
if err = p.TykAPISpec.GraphQLExecutor.CustomExecutor.Execute(reqCtx, gqlRequest, &resultWriter, execOptions...); err != nil {
if p.TykAPISpec.GraphQLExecutor.OtelExecutor != nil {
if err = p.TykAPISpec.GraphQLExecutor.OtelExecutor.Execute(reqCtx, gqlRequest, &resultWriter, execOptions...); err != nil {
return
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package gateway
package graphql

import (
"context"
"go.opentelemetry.io/otel/codes"
"sync"

"github.com/sirupsen/logrus"
Expand All @@ -17,36 +18,47 @@ import (
type OtelGraphqlEngineV2 struct {
mutex sync.Mutex
logger *logrus.Entry
spec *APISpec
traceContext context.Context
tracerProvider otel.TracerProvider

engine *graphql.ExecutionEngineV2
rootExecutor graphql.ExecutionEngineV2Executor
}

func (o *OtelGraphqlEngineV2) setContext(ctx context.Context) {
func (o *OtelGraphqlEngineV2) SetContext(ctx context.Context) {
o.mutex.Lock()
defer o.mutex.Unlock()
o.traceContext = ctx
}

func (o *OtelGraphqlEngineV2) setRootExecutor(executor graphql.ExecutionEngineV2Executor) {
func (o *OtelGraphqlEngineV2) SetRootExecutor(executor graphql.ExecutionEngineV2Executor) {
o.rootExecutor = executor
}

func (o *OtelGraphqlEngineV2) Normalize(operation *graphql.Request) error {
var operationName = "NormalizeRequest"
_, span := o.tracerProvider.Tracer().Start(o.traceContext, operationName)
defer span.End()
return o.engine.Normalize(operation)
err := o.engine.Normalize(operation)
if err != nil {
span.SetStatus(codes.Error, "request normalization failed")
return err
}
span.SetStatus(codes.Ok, "success")
return nil
}

func (o *OtelGraphqlEngineV2) ValidateForSchema(operation *graphql.Request) error {
var operationName = "ValidateRequest"
_, span := o.tracerProvider.Tracer().Start(o.traceContext, operationName)
defer span.End()
return o.engine.ValidateForSchema(operation)
err := o.engine.ValidateForSchema(operation)
if err != nil {
span.SetStatus(codes.Error, "request validation failed")
return err
}
span.SetStatus(codes.Ok, "success")
return nil
}

func (o *OtelGraphqlEngineV2) Setup(ctx context.Context, postProcessor *postprocess.Processor, resolveContext *resolve.Context, operation *graphql.Request, options ...graphql.ExecutionOptionsV2) {
Expand All @@ -60,15 +72,26 @@ func (o *OtelGraphqlEngineV2) Plan(postProcessor *postprocess.Processor, operati
var operationName = "GeneratePlan"
_, span := o.tracerProvider.Tracer().Start(o.traceContext, operationName)
defer span.End()
return o.engine.Plan(postProcessor, operation, report)
plan, err := o.engine.Plan(postProcessor, operation, report)
if err != nil {
span.SetStatus(codes.Error, "failed to generate plan")
return nil, err
}
span.SetStatus(codes.Ok, "success")
return plan, nil
}

func (o *OtelGraphqlEngineV2) Resolve(resolveContext *resolve.Context, planResult plan.Plan, writer resolve.FlushWriter) error {
var operationName = "ResolvePlan"
ctx, span := o.tracerProvider.Tracer().Start(o.traceContext, operationName)
defer span.End()
resolveContext.Context = ctx
return o.engine.Resolve(resolveContext, planResult, writer)
if err := o.engine.Resolve(resolveContext, planResult, writer); err != nil {
span.SetStatus(codes.Error, "failed to resolve")
return err
}
span.SetStatus(codes.Ok, "success")
return nil
}

func (o *OtelGraphqlEngineV2) Teardown() {
Expand All @@ -78,14 +101,24 @@ func (o *OtelGraphqlEngineV2) InputValidation(operation *graphql.Request) error
var operationName = "InputValidation"
_, span := o.tracerProvider.Tracer().Start(o.traceContext, operationName)
defer span.End()
return o.engine.InputValidation(operation)
if err := o.engine.InputValidation(operation); err != nil {
span.SetStatus(codes.Error, "failed input validation")
return err
}
span.SetStatus(codes.Ok, "success")
return nil
}

func (o *OtelGraphqlEngineV2) Execute(inCtx context.Context, operation *graphql.Request, writer resolve.FlushWriter, options ...graphql.ExecutionOptionsV2) error {
ctx, span := o.tracerProvider.Tracer().Start(inCtx, "GraphqlEngine")
defer span.End()
o.setContext(ctx)
return o.rootExecutor.Execute(inCtx, operation, writer, options...)
o.SetContext(ctx)
if err := o.engine.Execute(inCtx, operation, writer, options...); err != nil {
span.SetStatus(codes.Error, "failed to execute")
return err
}
span.SetStatus(codes.Ok, "success")
return nil
}

func NewOtelGraphqlEngineV2(tracerProvider otel.TracerProvider, engine *graphql.ExecutionEngineV2) *OtelGraphqlEngineV2 {
Expand Down

0 comments on commit a1d286a

Please sign in to comment.