Skip to content

Instantly share code, notes, and snippets.

@maxcan
Created February 3, 2022 20:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxcan/609e575b0acd89df9228041d9e09fac8 to your computer and use it in GitHub Desktop.
Save maxcan/609e575b0acd89df9228041d9e09fac8 to your computer and use it in GitHub Desktop.
Upload gql files to hasura allowed list
#!/usr/bin/env ./node_modules/.bin/ts-node-script
import { $, argv, fs } from "zx";
import { parse, print, visit, DefinitionNode } from "graphql";
import axios from "axios";
const MAKE_ENV = process.env.MAKE_ENV;
if (!MAKE_ENV) {
const err = "Misisng MAKE_ENV";
console.error(err);
throw err;
}
require("dotenv").config({ path: `.env.${process.env.MAKE_ENV}` });
function test() {
$`echo ENV: ${process.env.SUBDOMAIN_APP_ADMIN}`;
}
async function addGqlToAllowed() {
let file = argv["file"];
let name = argv["name"];
if (!name) {
throw "Missing name arg";
}
if (!file) {
throw "Missing file arg";
}
$`echo using file ${argv["file"]}`;
const gqlData = fs.readFileSync(file).toString();
const parsed = parseQueryString(gqlData);
// console.log(parsed);
try {
let _ = await axios({
method: "POST",
url: `https://${process.env.FQDN_HASURA}/v1/metadata`,
headers: {
"x-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET!,
},
data: {
type: "drop_query_collection",
args: {
collection: name,
cascade: true,
},
},
});
} catch (e) {
console.error(e);
throw e;
}
let _ = await axios({
method: "POST",
url: `https://${process.env.FQDN_HASURA}/v1/metadata`,
headers: {
"x-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET!,
},
data: {
type: "create_query_collection",
args: {
name,
definition: {
queries: parsed,
},
},
},
});
_ = await axios({
method: "POST",
url: `https://${process.env.FQDN_HASURA}/v1/metadata`,
headers: {
"x-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET!,
},
data: {
type: "add_collection_to_allowlist",
args: {
collection: name,
},
},
});
}
console.log(argv);
const cmd = argv["_"][0];
switch (cmd) {
case "add-gql-to-allowed":
addGqlToAllowed();
break;
case "test":
test();
break;
default:
const err = "unsupported command: " + cmd;
console.error(err);
throw err;
}
// ----- Utility functions
export type NewDefinitionNode = DefinitionNode & {
name?: {
value: string;
};
};
function parseQueryString(queryString: string) {
const queries: { name: string; query: string }[] = [];
let parsedQueryString;
try {
parsedQueryString = parse(queryString);
} catch (ex) {
throw new Error("Parsing operation failed");
}
const definitions: NewDefinitionNode[] = [...parsedQueryString.definitions];
const definitionHash = (definitions || []).reduce(
(defObj: Record<string, NewDefinitionNode>, queryObj) => {
if (queryObj.name) defObj[queryObj.name.value] = queryObj;
return defObj;
},
{}
);
const queryDefs = definitions.filter(
(def) => def.kind === "OperationDefinition"
);
const fragmentDefs = definitions.filter(
(def) => def.kind === "FragmentDefinition"
);
queryDefs.forEach((queryDef) => {
const queryName = queryDef.name ? queryDef.name.value : `unnamed`;
const query = {
name: queryName,
query: getQueryString(queryDef, fragmentDefs, definitionHash),
};
queries.push(query);
});
return queries;
}
function recurQueryDef(
queryDef: NewDefinitionNode,
fragments: Set<string>,
definitionHash: Record<string, any>
) {
visit(queryDef, {
FragmentSpread(node) {
fragments.add(node.name.value);
recurQueryDef(definitionHash[node.name.value], fragments, definitionHash);
},
});
}
function getQueryFragments(
queryDef: NewDefinitionNode,
definitionHash: Record<string, any> = {}
) {
const fragments = new Set<string>();
recurQueryDef(queryDef, fragments, definitionHash);
return [...Array.from(fragments)];
}
function getQueryString(
queryDef: NewDefinitionNode,
fragmentDefs: NewDefinitionNode[],
definitionHash: Record<string, any> = {}
) {
let queryString = print(queryDef);
const queryFragments = getQueryFragments(queryDef, definitionHash);
queryFragments.forEach((qf) => {
// eslint-disable-next-line array-callback-return
const fragmentDef = fragmentDefs.find((fd) => {
if (fd.name) return fd.name.value === qf;
});
if (fragmentDef) {
queryString += `\n\n${print(fragmentDef)}`;
}
});
return queryString;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment