UNPKG

@betaflight/api-server

Version:

A GraphQL server to retreive data from betaflight flight controllers

125 lines 5.09 kB
/* eslint-disable react-hooks/rules-of-hooks */ import { ApolloError, ApolloServer } from "apollo-server-express"; import debug from "debug"; import express from "express"; import http from "http"; import ws from "ws"; import { useServer } from "graphql-ws/lib/use/ws"; import { execute, parse, subscribe } from "graphql"; import getPort from "get-port"; import { GRAPHQL_WS, SubscriptionServer } from "subscriptions-transport-ws"; import { GRAPHQL_TRANSPORT_WS_PROTOCOL } from "graphql-ws"; import { schema, context, mockedDeviceContext, startMockDevice, } from "@betaflight/api-graph"; const log = debug("api-graph:errors"); // eslint-disable-next-line import/prefer-default-export export const createServer = ({ mocked, persistedQueries, artifactsDirectory = `${__dirname}/artifacts/`, } = {}) => { const persistedQueriesStore = persistedQueries ? Object.fromEntries(Object.entries(persistedQueries).map(([id, query]) => [ id, parse(query), ])) : undefined; const app = express(); const server = http.createServer(app); app.use("/job-artifacts", express.static(artifactsDirectory)); const contextGenerator = mocked ? mockedDeviceContext({ artifactsDir: artifactsDirectory }) : context({ artifactsDir: artifactsDirectory }); // Create a graphql server which will work with // graphql-ws const graphqlWsServer = new ws.Server({ noServer: true, }); useServer({ context: contextGenerator, onSubscribe: persistedQueriesStore ? (_ctx, msg) => { const document = persistedQueriesStore[msg.payload.query]; if (!document) { // for extra security you only allow the queries from the store throw new ApolloError("404: Query Not Found"); } return { document, schema, variableValues: msg.payload.variables, }; } : undefined, schema, execute: async (args) => { var _a; const result = await execute(args); (_a = result.errors) === null || _a === void 0 ? void 0 : _a.filter((e) => !e.message.includes("not open") && !e.message.includes("is not active")).forEach((error) => log(error)); return result; }, subscribe, }, graphqlWsServer); // And one which works with legacy protocol (subscriptions-transport-ws) const subscriptionTransportWsServer = new ws.Server({ noServer: true, }); SubscriptionServer.create({ schema, execute, subscribe, onConnect: () => contextGenerator(), }, subscriptionTransportWsServer); // And create an apollo http server which // will handle HTTP traffic const apolloServer = new ApolloServer({ schema, context: contextGenerator, formatError: (error) => { log(error); return error; }, }); // route to the correct graphql server // based on the received protocol server.on("upgrade", (req, socket, head) => { // extract websocket subprotocol from header const protocol = req.headers["sec-websocket-protocol"]; const protocols = Array.isArray(protocol) ? protocol : protocol === null || protocol === void 0 ? void 0 : protocol.split(",").map((p) => p.trim()); // decide which websocket server to use const wss = (protocols === null || protocols === void 0 ? void 0 : protocols.includes(GRAPHQL_WS)) && // subscriptions-transport-ws subprotocol !protocols.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL) // graphql-ws subprotocol ? subscriptionTransportWsServer : // graphql-ws will welcome its own subprotocol and // gracefully reject invalid ones. if the client supports // both transports, graphql-ws will prevail graphqlWsServer; wss.handleUpgrade(req, socket, head, (s) => { wss.emit("connection", s, req); }); }); return { schema, context: contextGenerator, startMockTicks: startMockDevice, apolloServer, rest: app, listen: async ({ port, hostname }) => { const listeningPort = port !== null && port !== void 0 ? port : (await getPort({ port: 9000, host: hostname })); await apolloServer.start(); apolloServer.applyMiddleware({ app }); return new Promise((resolve, reject) => { try { server.listen(listeningPort, hostname, () => { if (mocked) { startMockDevice(); } resolve(listeningPort); }); } catch (e) { reject(e); } }); }, }; }; //# sourceMappingURL=index.js.map