Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions router-tests/graphql_over_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/stretchr/testify/require"
"github.com/wundergraph/cosmo/router-tests/testenv"
"github.com/wundergraph/cosmo/router/core"
)

func TestOperationsOverGET(t *testing.T) {
Expand Down Expand Up @@ -54,6 +55,97 @@ func TestOperationsOverGET(t *testing.T) {
require.Equal(t, `{"errors":[{"message":"Mutations can only be sent over HTTP POST"}]}`, res.Body)
})
})

t.Run("Query should be successful with custom path", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
OverrideGraphQLPath: "/custom-graphql",
}, func(t *testing.T, xEnv *testenv.Environment) {
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
OperationName: []byte(`Employees`),
Query: `query Employees { employees { id } }`,
})
require.NoError(t, err)
require.Equal(t, http.StatusOK, res.Response.StatusCode)
require.Equal(t, `{"data":{"employees":[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":7},{"id":8},{"id":10},{"id":11},{"id":12}]}}`, res.Body)
})
})

t.Run("Mutation should not be allowed with custom path", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
OverrideGraphQLPath: "/custom-graphql",
}, func(t *testing.T, xEnv *testenv.Environment) {
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
OperationName: []byte(`updateEmployeeTag`),
Query: "mutation updateEmployeeTag {\n updateEmployeeTag(id: 10, tag: \"dd\") {\n id\n }\n}",
})
require.NoError(t, err)
require.Equal(t, http.StatusMethodNotAllowed, res.Response.StatusCode)
require.Equal(t, `{"errors":[{"message":"Mutations can only be sent over HTTP POST"}]}`, res.Body)
})
})

t.Run("Should return 404 for unknown path", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
RouterOptions: []core.Option{
core.WithGraphQLPath("/custom-graphql"),
},
}, func(t *testing.T, xEnv *testenv.Environment) {
// Default path for creating requests is /graphql if not updated with `OverrideGraphQLPath`
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
OperationName: []byte(`Employees`),
Query: `query Employees { employees { id } }`,
})

require.NoError(t, err)
require.Equal(t, http.StatusNotFound, res.Response.StatusCode)
})
})

t.Run("Should not create wildcard for root path", func(t *testing.T) {
Comment thread
StarpTech marked this conversation as resolved.
t.Parallel()

testenv.Run(t, &testenv.Config{
RouterOptions: []core.Option{
core.WithGraphQLPath("/"),
},
}, func(t *testing.T, xEnv *testenv.Environment) {
// Default path for creating requests is /graphql if not updated with `OverrideGraphQLPath`
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
OperationName: []byte(`Employees`),
Query: `query Employees { employees { id } }`,
})

require.NoError(t, err)
require.Equal(t, http.StatusNotFound, res.Response.StatusCode)
})
})

t.Run("Should allow to create wildcard path", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
RouterOptions: []core.Option{
core.WithGraphQLPath("/*"),
},
}, func(t *testing.T, xEnv *testenv.Environment) {
// Default path for creating requests is /graphql if not updated with `OverrideGraphQLPath`
res, err := xEnv.MakeGraphQLRequestOverGET(testenv.GraphQLRequest{
OperationName: []byte(`Employees`),
Query: `query Employees { employees { id } }`,
})

require.NoError(t, err)
require.Equal(t, http.StatusOK, res.Response.StatusCode)
require.Equal(t, `{"data":{"employees":[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":7},{"id":8},{"id":10},{"id":11},{"id":12}]}}`, res.Body)

})
})
}

func TestSubscriptionOverGET(t *testing.T) {
Expand Down Expand Up @@ -143,4 +235,48 @@ func TestSubscriptionOverGET(t *testing.T) {
wg.Wait()
})
})

t.Run("should create subscription for custom graphql path", func(t *testing.T) {
t.Parallel()

type currentTimePayload struct {
Data struct {
CurrentTime struct {
UnixTime float64 `json:"unixTime"`
Timestamp string `json:"timestamp"`
} `json:"currentTime"`
} `json:"data"`
}

testenv.Run(t, &testenv.Config{
OverrideGraphQLPath: "/custom-graphql",
}, func(t *testing.T, xEnv *testenv.Environment) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

var wg sync.WaitGroup
wg.Add(2)

go xEnv.GraphQLSubscriptionOverSSEWithQueryParam(ctx, testenv.GraphQLRequest{
OperationName: []byte(`CurrentTime`),
Query: `subscription CurrentTime { currentTime { unixTime timeStamp }}`,
Header: map[string][]string{
"Content-Type": {"application/json"},
"Connection": {"keep-alive"},
"Cache-Control": {"no-cache"},
},
}, func(data string) {
defer wg.Done()

var payload currentTimePayload
err := json.Unmarshal([]byte(data), &payload)
require.NoError(t, err)

require.NotZero(t, payload.Data.CurrentTime.UnixTime)
require.NotEmpty(t, payload.Data.CurrentTime.Timestamp)
})

wg.Wait()
})
})
}
79 changes: 79 additions & 0 deletions router-tests/graphql_over_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,83 @@ func TestGraphQLOverHTTPCompatibility(t *testing.T) {
require.Equal(t, http.StatusOK, res.StatusCode)
})
})

t.Run("requests with custom Path", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
OverrideGraphQLPath: "/custom-graphql",
}, func(t *testing.T, xEnv *testenv.Environment) {
t.Run("valid request should return 200 with custom path", func(t *testing.T) {
t.Parallel()
header := http.Header{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
}
body := []byte(`{"query":"query Find($criteria: SearchInput!) {findEmployees(criteria: $criteria){id details {forename surname}}}","variables":{"criteria":{"nationality":"GERMAN"}}}`)
res, err := xEnv.MakeRequest("POST", "/custom-graphql", header, bytes.NewReader(body))
require.NoError(t, err)
require.Equal(t, http.StatusOK, res.StatusCode)
data, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, `{"data":{"findEmployees":[{"id":1,"details":{"forename":"Jens","surname":"Neuse"}},{"id":2,"details":{"forename":"Dustin","surname":"Deus"}},{"id":4,"details":{"forename":"Björn","surname":"Schwenzer"}},{"id":11,"details":{"forename":"Alexandra","surname":"Neuse"}}]}}`, string(data))
})

t.Run("valid request should return 404 with custom path", func(t *testing.T) {
t.Parallel()
header := http.Header{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
}
body := []byte(`{"query":"query Find($criteria: SearchInput!) {findEmployees(criteria: $criteria){id details {forename surname}}}","variables":{"criteria":{"nationality":"GERMAN"}}}`)
res, err := xEnv.MakeRequest("POST", "/graphql", header, bytes.NewReader(body))
require.NoError(t, err)
require.Equal(t, http.StatusNotFound, res.StatusCode)
})

})

testenv.Run(t, &testenv.Config{
RouterOptions: []core.Option{
core.WithGraphQLPath("/*"),
},
}, func(t *testing.T, xEnv *testenv.Environment) {
t.Run("valid request should return status 200 when wildcard was defined for path", func(t *testing.T) {
t.Parallel()

header := http.Header{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
}

body := []byte(`{"query":"query Find($criteria: SearchInput!) {findEmployees(criteria: $criteria){id details {forename surname}}}","variables":{"criteria":{"nationality":"GERMAN"}}}`)
res, err := xEnv.MakeRequest("POST", "/graphql", header, bytes.NewReader(body))
require.NoError(t, err)
require.Equal(t, http.StatusOK, res.StatusCode)
data, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, `{"data":{"findEmployees":[{"id":1,"details":{"forename":"Jens","surname":"Neuse"}},{"id":2,"details":{"forename":"Dustin","surname":"Deus"}},{"id":4,"details":{"forename":"Björn","surname":"Schwenzer"}},{"id":11,"details":{"forename":"Alexandra","surname":"Neuse"}}]}}`, string(data))
})
})

testenv.Run(t, &testenv.Config{
RouterOptions: []core.Option{
core.WithGraphQLPath("/"),
},
}, func(t *testing.T, xEnv *testenv.Environment) {
t.Run("valid request should return status 404 when no wildcard was defined on root path", func(t *testing.T) {
t.Parallel()

header := http.Header{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
}

body := []byte(`{"query":"query Find($criteria: SearchInput!) {findEmployees(criteria: $criteria){id details {forename surname}}}","variables":{"criteria":{"nationality":"GERMAN"}}}`)
res, err := xEnv.MakeRequest("POST", "/graphql", header, bytes.NewReader(body))
require.NoError(t, err)
require.Equal(t, http.StatusNotFound, res.StatusCode)
})
})
})
}
8 changes: 4 additions & 4 deletions router/core/graph_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ func newGraphServer(ctx context.Context, r *Router, routerConfig *nodev1.RouterC
})

// Mount the feature flag handler. It calls the base mux if no feature flag is set.
cr.Mount(r.graphqlPath, multiGraphHandler)
cr.Handle(r.graphqlPath, multiGraphHandler)

if r.webSocketConfiguration != nil && r.webSocketConfiguration.Enabled && r.webSocketConfiguration.AbsintheProtocol.Enabled {
// Mount the Absinthe protocol handler for WebSockets
httpRouter.Mount(r.webSocketConfiguration.AbsintheProtocol.HandlerPath, multiGraphHandler)
httpRouter.Handle(r.webSocketConfiguration.AbsintheProtocol.HandlerPath, multiGraphHandler)
}
})

Expand Down Expand Up @@ -1081,9 +1081,9 @@ func (s *graphServer) buildGraphMux(ctx context.Context,
httpRouter.Use(s.routerMiddlewares...)

// GraphQL over POST
httpRouter.Post("/", graphqlHandler.ServeHTTP)
httpRouter.Post(s.graphqlPath, graphqlHandler.ServeHTTP)
// GraphQL over GET
httpRouter.Get("/", graphqlHandler.ServeHTTP)
httpRouter.Get(s.graphqlPath, graphqlHandler.ServeHTTP)

gm.mux = httpRouter

Expand Down