Skip to content

Commit

Permalink
feat: better error messages when resolving undefined variables (#419)
Browse files Browse the repository at this point in the history
* feat: better error messages when resolving undefined variables

Test before calling resolveVariable(), since by that point the information
has already been lost and it's too late to provide a more detailed message

Fixes #262

* feat: include API namespace in missing variable errors

Helps finding where the error is

* chore: reword error messages, introduce custom Error type
  • Loading branch information
fiam committed Dec 20, 2022
1 parent 83df2b8 commit aa0dd65
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/sdk/src/configure/variables.ts
Expand Up @@ -29,7 +29,7 @@ export type InputVariable<T = string> = T | EnvironmentVariable<T> | PlaceHolder
*/
export const resolveVariable = (variable: string | EnvironmentVariable): string => {
if (variable === undefined) {
throw new Error(`could not resolve empty data variable: ${variable}`);
throw new Error('could not resolve undefined data variable');
}
if (variable instanceof EnvironmentVariable) {
const environmentVariable = variable as EnvironmentVariable;
Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/src/db/introspection.ts
Expand Up @@ -82,6 +82,9 @@ export const introspectPrismaDatabaseWithRetries = async (
databaseSchema: DatabaseSchema,
maxRetries: number
): Promise<PrismaDatabaseIntrospectionResult> => {
if (!introspection.databaseURL) {
throw new Error('database URL is not defined');
}
const resolvedURL = resolveVariable(introspection.databaseURL);
for (let i = 0; i < maxRetries; i++) {
try {
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/definition/federation-introspection.ts
Expand Up @@ -93,6 +93,7 @@ export const introspectFederation = async (introspection: GraphQLFederationIntro
}
const introspectionHeaders = resolveGraphqlIntrospectionHeaders(mapHeaders(introspectionHeadersBuilder));

// upstream.url is truthy at this point, no need to check
schema = await fetchFederationServiceSDL(resolveVariable(upstream.url), introspectionHeaders, {
apiNamespace: introspection.apiNamespace,
upstreamName: name,
Expand Down
34 changes: 28 additions & 6 deletions packages/sdk/src/definition/graphql-introspection.ts
Expand Up @@ -35,6 +35,13 @@ import { Logger } from '../logger';
import { mergeSchemas } from '@graphql-tools/schema';
import transformSchema from '../transformations/shema';

class MissingKeyError extends Error {
constructor(private key: string, private introspection: GraphQLIntrospection) {
super(`${key} is not defined in your ${introspection.apiNamespace} datasource`);
Object.setPrototypeOf(this, MissingKeyError.prototype);
}
}

export const resolveGraphqlIntrospectionHeaders = (headers?: { [key: string]: HTTPHeader }): Record<string, string> => {
const baseHeaders: Record<string, string> = {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -83,13 +90,19 @@ export const introspectGraphql = async (introspection: GraphQLIntrospection): Pr
const federationEnabled = isFederationService(schema);
const upstreamSchema = cleanupSchema(schema, introspection);
const { schemaSDL, customScalarTypeFields } = transformSchema.replaceCustomScalars(upstreamSchema, introspection);
const serviceSDL = !federationEnabled
? undefined
: introspection.loadSchemaFromString
? loadFile(introspection.loadSchemaFromString)
: await fetchFederationServiceSDL(resolveVariable(introspection.url), introspectionHeaders, {
let serviceSDL: string | undefined;
if (federationEnabled) {
if (introspection.loadSchemaFromString) {
serviceSDL = loadFile(introspection.loadSchemaFromString);
} else {
if (!introspection.url) {
throw new MissingKeyError('url', introspection);
}
serviceSDL = await fetchFederationServiceSDL(resolveVariable(introspection.url), introspectionHeaders, {
apiNamespace: introspection.apiNamespace,
});
});
}
}
const serviceDocumentNode = serviceSDL !== undefined ? parse(serviceSDL) : undefined;
const schemaDocumentNode = parse(schemaSDL);
const graphQLSchema = buildSchema(schemaSDL);
Expand Down Expand Up @@ -215,6 +228,12 @@ const introspectGraphQLAPI = async (
};

if (introspection.mTLS) {
if (!introspection.mTLS.key) {
throw new MissingKeyError('mTLS.key', introspection);
}
if (!introspection.mTLS.cert) {
throw new MissingKeyError('mTLS.cert', introspection);
}
opts.httpsAgent = new https.Agent({
key: resolveVariable(introspection.mTLS.key),
cert: resolveVariable(introspection.mTLS.cert),
Expand All @@ -224,6 +243,9 @@ const introspectGraphQLAPI = async (

let res: AxiosResponse | undefined;
try {
if (!introspection.url) {
throw new MissingKeyError('url', introspection);
}
res = await Fetcher().post(resolveVariable(introspection.url), data, opts);
} catch (e: any) {
throw new Error(
Expand Down

0 comments on commit aa0dd65

Please sign in to comment.