Skip to content

Commit

Permalink
2329 bring server exceptions into the new apollo world
Browse files Browse the repository at this point in the history
  • Loading branch information
lperson committed Jan 1, 2024
1 parent dc886e9 commit eb58abe
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/server/api/mutations/bulkSendMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const bulkSendMessages = async (
) {
log.error("Not allowed to send all messages at once");
throw new GraphQLError("Not allowed to send all messages at once", {
extensions: { status: 403 }
extensions: { http: { status: 403 } }
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/server/api/mutations/sendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const sendMessage = async (
// const offsetData = zipData ? { offset: zipData.timezone_offset, hasDST: zipData.has_dst } : null
// if (!isBetweenTextingHours(offsetData, config)) {
// throw new GraphQLError({
// status: 400,
// extensions: { http: { { status: 400 } },
// message: "Skipped sending because it's now outside texting hours for this contact"
// })
// }
Expand Down
6 changes: 3 additions & 3 deletions src/server/api/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ const rootMutations = {
if (!lastMessage) {
throw new GraphQLError(
"Cannot fake a reply to a contact that has no existing thread yet",
{ extensions: { status: 400 } }
{ extensions: { http: { status: 400 } } }
);
}

Expand Down Expand Up @@ -1053,7 +1053,7 @@ const rootMutations = {
) {
throw new GraphQLError(
"Not allowed to add contacts after the campaign starts",
{ extensions: { status: 400 } }
{ extensions: { http: { status: 400 } } }
);
}
return editCampaign(id, campaign, loaders, user, origCampaign);
Expand Down Expand Up @@ -1109,7 +1109,7 @@ const rootMutations = {
const invite = await Invite.get(inviteId);
if (!invite || !invite.is_valid) {
throw new GraphQLError("That invitation is no longer valid", {
extensions: { status: 400 }
extensions: { http: { status: 400 } }
});
}

Expand Down
109 changes: 59 additions & 50 deletions src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,72 +67,61 @@ const executableSchema = makeExecutableSchema({
allowUndefinedInResolve: false
});

const errorReportingPlugin = {
async requestDidStart() {
return {
async didEncounterErrors(requestContext) {
requestContext.errors.forEach(error => {
log.error({
userId: requestContext?.contextValue?.user?.id,
code: (error && error?.extensions?.code) || "INTERNAL_SERVER_ERROR",
error,
msg: "GraphQL error"
});
telemetry
.formatRequestError(error, requestContext.contextValue)
// drop if this fails
.catch(() => {})
.then(() => {});
});
}
};
}
};

const server = new ApolloServer({
resolvers,
schema: executableSchema,
introspection: true,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),

errorReportingPlugin,

// display the graphql IDE on the /graphql endpoint
// when invoked with no query and when not in production
process.env.NODE_ENV !== "production"
? ApolloServerPluginLandingPageLocalDefault({ embed: true })
: // no default landing page in production
ApolloServerPluginLandingPageDisabled()
]
],
formatError: (error, originalError) => {
if (process.env.SHOW_SERVER_ERROR || process.env.DEBUG) {
if (error instanceof GraphQLError) {
return error;
}
return new GraphQLError(error.message);
}

return new GraphQLError(
error && error?.extensions?.code === "UNAUTHORIZED"
? "UNAUTHORIZED"
: "Internal server error"
);
}
});

server.start().then(() => {
app.use(
"/graphql",
cors(),
express.json(),
expressMiddleware(server, {
schema: executableSchema,
context: async ({ request }) => ({
loaders: createLoaders(),
user: request.user,
awsContext: request.awsContext || null,
awsEvent: request.awsEvent || null,
remainingMilliseconds: () =>
request.awsContext && request.awsContext.getRemainingTimeInMillis
? request.awsContext.getRemainingTimeInMillis()
: 5 * 60 * 1000 // default saying 5 min, no matter what
})
// formatError: error => {
// log.error({
// userId: request.user && request.user.id,
// code:
// (error && error.originalError && error.originalError.code) ||
// "INTERNAL_SERVER_ERROR",
// error,
// msg: "GraphQL error"
// });
// telemetry
// .formatRequestError(error, request)
// // drop if this fails
// .catch(() => {})
// .then(() => {});

// if (process.env.SHOW_SERVER_ERROR || process.env.DEBUG) {
// if (error instanceof GraphQLError) {
// return error;
// }
// return new GraphQLError(error.message);
// }

// return new GraphQLError(
// error &&
// error.originalError &&
// error.originalError.code === "UNAUTHORIZED"
// ? "UNAUTHORIZED"
// : "Internal server error"
// );
// }
})
);

// Don't rate limit heroku
app.enable("trust proxy");

Expand Down Expand Up @@ -214,6 +203,26 @@ server.start().then(() => {
app.post("/login-callback", ...loginCallbacks.loginCallback);
}

app.use(
"/graphql",
cors(),
expressMiddleware(server, {
schema: executableSchema,
context: async request => {
return {
loaders: createLoaders(),
user: request.req.user,
awsContext: request.awsContext || null,
awsEvent: request.awsEvent || null,
remainingMilliseconds: () =>
request.awsContext && request.awsContext.getRemainingTimeInMillis
? request.awsContext.getRemainingTimeInMillis()
: 5 * 60 * 1000 // default saying 5 min, no matter what
};
}
})
);

// This middleware should be last. Return the React app only if no other route is hit.
app.use(appRenderer);

Expand Down
4 changes: 2 additions & 2 deletions src/server/telemetry.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ async function reportError(err, details) {

async function formatRequestError(err, req) {
let code = "UNKNOWN";
if (err.originalError) {
code = err.originalError.code || "INTERNAL_SERVER_ERROR";
if (err) {
code = err?.extensions?.code || "INTERNAL_SERVER_ERROR";
}
err.code = code;
await Promise.all(formatRequestErrorCallbacks.map(cb => cb(err, req)));
Expand Down

0 comments on commit eb58abe

Please sign in to comment.