Skip to content

Commit

Permalink
feat: add schemaUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrrt committed Nov 17, 2023
1 parent c750ded commit 3890497
Show file tree
Hide file tree
Showing 8 changed files with 1,704 additions and 3,336 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
"eslint": "^8.48.0",
"prettier": "^3.0.3",
"ts-jest": "^29.1.1",
"tsconfig": "workspace:*",
"tsup": "^7.2.0",
"turbo": "latest"
},
"packageManager": "[email protected]",
"name": "hydra",
"dependencies": {
"jest": "^29.7.0"
"@graphql-tools/schema": "^10.0.0",
"graphql-yoga": "^5.0.0",
"jest": "^29.7.0",
"node-fetch": "^3.3.2"
}
}
1 change: 1 addition & 0 deletions packages/core/src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
schemaRawGenerated.ts
30 changes: 29 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
console.log("core");
import { createYoga } from "graphql-yoga";
import { schema } from "./schema";

export const GraphQLHandler = async (req: Request, env: any, ctx: any) => {
const yoga = createYoga({
schema,
context: async (event) => {
const { request } = event;
const authorization =
(request.headers.get("Authorization") ||
request.headers.get("authorization")) ??
null;

const forwardedFor = request.headers.get("x-forwarded-for");
// "x-forwarded-for": "\"[2a02:1210:5274:9f00:b94e:d462:5886:5cc2]\", 140.248.74.113, 140.248.74.113",
// extract ip address in [] in forwardedFor
const remoteIp = forwardedFor?.match(/\[(.*?)\]/)?.[1] ?? null;

return {
// request: modifiedRequest, // Pass the modified request to the context
authorization,
remoteIp,
userAgent: request.headers.get("user-agent") ?? null,
};
},
landingPage: false,
});
return yoga(req, env, ctx);
};
154 changes: 154 additions & 0 deletions packages/core/src/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { makeExecutableSchema } from "@graphql-tools/schema";
import { default as fetch } from "node-fetch";
import * as schemasGenerated from "./schemaRawGenerated";

const schemas = Object.values(schemasGenerated);

import {
addTypenameKeywordToSchema,
buildGraphQLSchemaSDL,
unifyGraphQLSchemasObjects,
} from "./schemaUtils";

const fetchGraphQLEndpoint = async (
endpoint: string,
query: string,
variables: object = {},
methodId: string,
headers: object = {}
) => {
const requestBody = {
query,
variables,
};

try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
...headers,
},
body: JSON.stringify(requestBody),
});

if (!response.ok) {
throw new Error(
`Network response was not ok: ${response.status} ${response.statusText}`
);
}

const json: any = await response.json();
if (json.errors) {
throw new Error(JSON.stringify(json.errors));
}
return json.data[methodId];
} catch (error) {
throw error;
}
};

// extract everything between type Query {}
const queryRegex = /type Query {([\s\S]*?)}/;
const mutationRegex = /type Mutation {([\s\S]*?)}/;

const extractPrimitiveWithRegExp = (rawSchema: string, regex: RegExp) => {
const primitiveMatch = rawSchema.match(regex);
const primitiveBody = primitiveMatch?.[1];
const primitiveBodyTrimmed = primitiveBody?.trim();
const primitiveBodyTrimmedLines = primitiveBodyTrimmed?.split("\n");
const operations = primitiveBodyTrimmedLines?.map((line) => {
const lineTrimmed = line.trim();
const lineTrimmedWords = lineTrimmed.split(" ");
let operationName = lineTrimmedWords?.[0];
// remove everything after : or ( (inclusive) in mutationName
// health: -> health
// health(meta: SomeInput) -> health
operationName = operationName?.replace(/[:\(].*$/, "");
return {
operationName,
};
});
return operations;
};

const getAuthorizationFromContext = (ctx) => {
const authorization = ctx?.request?.headers?.get("authorization");
return authorization;
};

const queries = [
...schemas
.map(({ introspected, url }) => {
return extractPrimitiveWithRegExp(introspected, queryRegex).map(
({ operationName }) => {
return {
id: operationName,
resolver: async (_, __, ctx) => {
const queryBody = ctx?.params?.query;
const variables = ctx?.params?.variables;
const authorization = getAuthorizationFromContext(ctx);
return await fetchGraphQLEndpoint(
url,
addTypenameKeywordToSchema(queryBody),
variables,
operationName,
{ Authorization: authorization }
);
},
};
}
);
})
.flat(),
];

const mutations = [
...schemas
.map(({ introspected, url }) => {
return extractPrimitiveWithRegExp(introspected, mutationRegex).map(
({ operationName }) => {
return {
id: operationName,
resolver: async (_, bindings, ctx) => {
const queryBody = ctx?.params?.query;
const variables = ctx?.params?.variables;
const authorization = getAuthorizationFromContext(ctx);
return await fetchGraphQLEndpoint(
url,
addTypenameKeywordToSchema(queryBody),
variables,
operationName,
{ Authorization: authorization }
);
},
};
}
);
})
.flat(),
];

const resolvers = {
Query: {},
Mutation: {},
};

queries?.forEach(({ id, resolver }) => {
resolvers.Query[id] = resolver;
});

mutations?.forEach(({ id, resolver }) => {
resolvers.Mutation[id] = resolver;
});

let schema = makeExecutableSchema({
typeDefs: buildGraphQLSchemaSDL(
unifyGraphQLSchemasObjects([
...schemas.map(({ introspected }) => introspected),
])
),
resolvers,
});

export { schema };
Loading

0 comments on commit 3890497

Please sign in to comment.