Skip to content

Commit

Permalink
Enable to configure allowed HTTP methods for subscriptions (#225)
Browse files Browse the repository at this point in the history
* Enable to configure allowed HTTP methods for subscriptions

* Move test

* Add changeset

* Update packages/core/lib/process-request.ts

Co-authored-by: Laurin Quast <laurinquast@googlemail.com>

* adjust test

Co-authored-by: Laurin Quast <laurinquast@googlemail.com>
  • Loading branch information
dan-lee and n1ru4l committed Mar 8, 2022
1 parent e534015 commit baf0b15
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-icons-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"graphql-helix": minor
---

Enable to configure allowed HTTP methods for subscriptions.
19 changes: 15 additions & 4 deletions packages/core/lib/process-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const processRequest = async <TContext = {}, TRootValue = {}>(
validate = defaultValidate,
validationRules,
variables,
allowedSubscriptionHttpMethods = ["GET", "POST"],
} = options;

let context: TContext | undefined;
Expand Down Expand Up @@ -165,10 +166,20 @@ export const processRequest = async <TContext = {}, TRootValue = {}>(
rootValue = rootValueFactory ? await rootValueFactory(executionContext) : ({} as TRootValue);

if (operation.operation === "subscription") {
if (!isHttpMethod("GET", request.method)) {
throw new HttpError(405, "Can only perform subscription operation from a GET request.", {
headers: [...defaultSingleResponseHeaders, { name: "Allow", value: "GET" }],
});
if (!allowedSubscriptionHttpMethods.some((method) => isHttpMethod(method, request.method))) {
throw new HttpError(
405,
`Can only perform subscription operation from a ${allowedSubscriptionHttpMethods.join(" or ")} request.`,
{
headers: [
...defaultSingleResponseHeaders,
{
name: "Allow",
value: allowedSubscriptionHttpMethods.join(", "),
},
],
}
);
}
const result = await subscribe({
schema,
Expand Down
4 changes: 4 additions & 0 deletions packages/core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ export interface ProcessRequestOptions<TContext, TRootValue> {
* Values for any Variables defined by the Operation.
*/
variables?: string | { [name: string]: any };
/**
* HTTP methods that are allowed for subscriptions.
*/
allowedSubscriptionHttpMethods?: ReadonlyArray<"POST" | "GET">;
}

export interface FormatPayloadParams<TContext, TRootValue> {
Expand Down
49 changes: 49 additions & 0 deletions packages/core/test/process-request.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getGraphQLParameters, processRequest } from "../lib";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { GraphQLError } from "graphql";

const schema = makeExecutableSchema({
typeDefs: /* GraphQL */ `
type Query {
hello: String
}
type Subscription {
countdown(from: Int): Int
}
`,
resolvers: {
Subscription: {
countdown: {
subscribe: async function* () {
yield "Hi";
},
},
},
},
});

describe("process-request", () => {
it("should not allow POST if disabled", async () => {
const request = {
body: { query: "subscription { countdown }" },
method: "POST",
headers: {},
query: "",
};

const { operationName, query, variables } = getGraphQLParameters(request);

const result = await processRequest({
operationName,
query,
variables,
request,
schema,
allowedSubscriptionHttpMethods: ["GET"],
});

expect(result.type).toBe("RESPONSE");
expect((result as any).status).toBe(405);
expect((result as any).payload.errors[0] instanceof GraphQLError).toBe(true);
});
});

0 comments on commit baf0b15

Please sign in to comment.