-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: outrospector v0 ok * feat: outrospection v1 ok * fix: outrospection arg type nesting * refactor: rename outrospector to outrospect * feat: add query&mutation type names to outrospection * feat: formatter & converter * refactor: clean lib.ts * refactor: re-organize directories and update cli before merge * feat: update readme for v1.1.0 * fix: lint before merge
- Loading branch information
Showing
10 changed files
with
665 additions
and
373 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { parse } from "https://deno.land/std@0.149.0/flags/mod.ts"; | ||
import { saveJsonFormatted } from "./lib.ts"; | ||
import { ensureDirSync } from "https://deno.land/std@0.151.0/fs/mod.ts"; | ||
import { createPostmanCollection } from "./index.ts"; | ||
|
||
// @TODO: improve the CLI | ||
|
||
function help() { | ||
console.log(`Error: not enough arguments. | ||
Usage: | ||
deno run index.ts <GRAPHQL_ENDPOINT_URL> {--out=OUTPUT_FILE, --authorization=AUTHORIZATION_HEADER} | ||
Help: | ||
deno run index.ts [--help | -h] | ||
`); | ||
} | ||
|
||
const args = parse(Deno.args, { boolean: ["help", "h"] }) as { | ||
_: [string]; | ||
help?: boolean; | ||
h?: boolean; | ||
out?: string; | ||
auth?: string; | ||
}; | ||
|
||
if (Deno.args.length < 1 || args.help || args.h) { | ||
help(); | ||
Deno.exit(1); | ||
} | ||
|
||
const url = args._[0]; | ||
let path = args.out; | ||
const authorization = args.auth; | ||
|
||
const urlRegexp = /https?:\/\/*/; | ||
if (!urlRegexp.test(url)) { | ||
console.error(`${url} is not a valid url`); | ||
Deno.exit(1); | ||
} | ||
console.log(`Creating the postman collection for ${url}`); | ||
|
||
const collection = await createPostmanCollection(url, authorization); | ||
|
||
path = path || "./out/" + collection.info.name + ".postman_collection.json"; | ||
ensureDirSync("./out/"); | ||
saveJsonFormatted(collection, path); | ||
console.log(`Collection saved at ${path}`); | ||
|
||
console.log(`Import it in postman and complete the queries ! 🚀`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
import { parse, print } from "https://esm.sh/v90/graphql@16.5.0"; | ||
import { Argument, ObjectField, Outrospection, Query } from "./outrospector.ts"; | ||
|
||
export interface FormattedArgument { | ||
formattedType: string; | ||
formattedVariable: string; | ||
defaultValue: string | "null" | "#"; | ||
} | ||
|
||
export interface FormattedField { | ||
formatedField: string; | ||
tempField: string; // a hash that will be replaced by the formatedField, after the query is parsed, to keep comments | ||
} | ||
|
||
export interface FormattedQuery { | ||
args: FormattedArgument[]; | ||
fields: FormattedField[]; | ||
fullQuery: string; | ||
variables: string; | ||
outrospectQuery: Query; | ||
} | ||
|
||
export interface QueryCollection { | ||
queries: FormattedQuery[]; | ||
mutations: FormattedQuery[]; | ||
} | ||
|
||
// @TODO: rework this | ||
function getDefaultValue(type: string) { | ||
switch (type) { | ||
case "ID": | ||
return `"0"`; | ||
case "STRING" || "String": | ||
return `""`; | ||
case "INT" || "Int": | ||
return `0`; | ||
case "FLOAT": | ||
return `0.0`; | ||
case "BOOLEAN": | ||
return `false`; | ||
case "INPUT_OBJECT": | ||
return `{}`; | ||
default: | ||
return `null`; | ||
} | ||
} | ||
|
||
function nestedArgToString(arg: Argument, argStr?: string): string { | ||
if (!argStr) argStr = arg.typeName; | ||
if (arg.nesting.length === 0) { | ||
return argStr; | ||
} else { | ||
const nesting = arg.nesting.pop(); | ||
if (nesting === "LIST") { | ||
return `[${nestedArgToString(arg, argStr)}]`; | ||
} else if (nesting === "NON_NULL") { | ||
return `${nestedArgToString(arg, argStr)}!`; | ||
} | ||
} | ||
return argStr; | ||
} | ||
|
||
function formatArgument(arg: Argument): FormattedArgument { | ||
const formattedType: string = nestedArgToString(arg); | ||
|
||
const defaultNonNullValue = formattedType | ||
.replace(arg.typeName, getDefaultValue(arg.typeName)) | ||
.replaceAll("!", ""); | ||
const defaultValue = formattedType.includes("!") | ||
? defaultNonNullValue | ||
: "null"; | ||
|
||
return { | ||
defaultValue, | ||
formattedType, | ||
formattedVariable: `\t"${arg.name}": ${defaultValue}`, | ||
}; | ||
} | ||
|
||
function formatField(field: ObjectField): FormattedField { | ||
let description = ""; | ||
if ( | ||
field.description && | ||
field.description !== "undefined" && | ||
field.description !== "" | ||
) { | ||
description = ` # ${field.description?.replace("\n", " ")}`; | ||
} | ||
|
||
function scalarFormat(field: ObjectField) { | ||
return `${field.name}${description}\n`; | ||
} | ||
|
||
function objectFormat(field: ObjectField) { | ||
return `# ${field.name}${description}\n`; | ||
} | ||
|
||
function othersFormat(field: ObjectField) { | ||
return `# ${field.name}${description} # Type: ${field.typeBaseKind}\n`; | ||
} | ||
|
||
let formattedFieldStr = ""; | ||
if (field.typeBaseKind === "SCALAR" || field.typeBaseKind === "ENUM") { | ||
formattedFieldStr = scalarFormat(field); | ||
} else if (field.typeBaseKind === "OBJECT") { | ||
formattedFieldStr = objectFormat(field); | ||
} else { | ||
formattedFieldStr = othersFormat(field); | ||
} | ||
|
||
const formattedField: FormattedField = { | ||
formatedField: formattedFieldStr, | ||
tempField: `_${crypto.randomUUID().split("-")[0]}\n`, | ||
}; | ||
|
||
return formattedField; | ||
} | ||
|
||
class FormattedFieldBuffer { | ||
formattedFields = new Map<string, FormattedField>(); | ||
outrospection: Outrospection; | ||
|
||
constructor(outrospection: Outrospection) { | ||
this.outrospection = outrospection; | ||
} | ||
|
||
formatField(field: ObjectField): FormattedField { | ||
if (this.formattedFields.get(field.name)) { | ||
return this.formattedFields.get(field.name) as FormattedField; | ||
} else { | ||
const formattedField = formatField(field); | ||
this.formattedFields.set(field.name, formattedField); | ||
return formattedField; | ||
} | ||
} | ||
} | ||
|
||
function formatQuery( | ||
query: Query, | ||
is: "query" | "mutation", | ||
outrospection: Outrospection, | ||
): FormattedQuery { | ||
let queryVarsDefinition = ""; | ||
let fieldVars = ""; | ||
let variables = ""; | ||
|
||
const formattedFieldBuffer = new FormattedFieldBuffer(outrospection); | ||
|
||
const formattedQuery: FormattedQuery = { | ||
args: [], | ||
fields: [], | ||
fullQuery: "", | ||
variables: "", | ||
outrospectQuery: query, | ||
}; | ||
|
||
Array.from(query.args).forEach(([_, arg], index) => { | ||
const formatedArg = formatArgument(arg); | ||
formattedQuery.args.push(formatedArg); | ||
queryVarsDefinition += `${index === 0 ? "" : ","}${ | ||
query.args.size > 3 ? "\n" : " " | ||
}$${arg.name}: ${formatedArg.formattedType}`; | ||
|
||
fieldVars += `${index === 0 ? "" : ", "}${ | ||
query.args.size > 3 ? "\n" : " " | ||
}${arg.name}: $${arg.name}`; | ||
|
||
variables += `${index === 0 ? "" : ",\n"}${formatedArg.formattedVariable}`; | ||
}); | ||
|
||
formattedQuery.variables = `{\n${variables}\n}`; | ||
|
||
if (query.args.size > 3) { | ||
queryVarsDefinition += "\n"; | ||
fieldVars += "\n"; | ||
} | ||
|
||
let formatedFields = "__typename\n"; | ||
|
||
const returnType = outrospection.types.get(query.typeName); | ||
if (!returnType) { | ||
throw new Error(`Type ${query.typeName} not found in outrospection`); | ||
} | ||
if (returnType && returnType.kind === "OBJECT") { | ||
returnType.fields?.forEach((field) => { | ||
const formatedField = formattedFieldBuffer.formatField(field); | ||
formattedQuery.fields.push(formatedField); | ||
formatedFields += formatedField.tempField; | ||
}); | ||
} | ||
|
||
const hasArgs = query.args.size > 0; | ||
const hasFields = returnType.kind === "OBJECT" && | ||
returnType.fields && returnType.fields?.length > 0; | ||
|
||
const parsed = parse( | ||
`${is} ${query.name}${ | ||
hasArgs ? `(${queryVarsDefinition})` : "" | ||
}{\n${query.name}${hasArgs ? `(${fieldVars})` : ""}${ | ||
hasFields ? `{\n${formatedFields}}` : "" | ||
}\n}`, | ||
); | ||
|
||
formattedQuery.fullQuery = print(parsed); | ||
|
||
if (returnType.kind === "OBJECT") { | ||
returnType.fields?.forEach((field) => { | ||
const formatedField = formattedFieldBuffer.formatField(field); | ||
formattedQuery.fullQuery = formattedQuery.fullQuery.replace( | ||
formatedField.tempField, | ||
formatedField.formatedField, | ||
); | ||
}); | ||
} | ||
|
||
return formattedQuery; | ||
} | ||
|
||
export function outrospectionToQueries( | ||
outrospection: Outrospection, | ||
): QueryCollection { | ||
const collection: QueryCollection = { | ||
queries: [], | ||
mutations: [], | ||
}; | ||
|
||
outrospection.queries.forEach((query) => { | ||
collection.queries.push(formatQuery(query, "query", outrospection)); | ||
}); | ||
|
||
outrospection.mutations.forEach((query) => { | ||
collection.queries.push(formatQuery(query, "mutation", outrospection)); | ||
}); | ||
|
||
return collection; | ||
} |
Oops, something went wrong.