Skip to content

Commit

Permalink
Merge pull request #3 from smartive/break/error-output
Browse files Browse the repository at this point in the history
break: Improve error logging and output
  • Loading branch information
dwirz authored Jul 19, 2023
2 parents 06395d7 + ff3e5b8 commit a4097e6
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 10 deletions.
4 changes: 2 additions & 2 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class UserInputError extends GraphQLError {
}

export class PermissionError extends ForbiddenError {
constructor(action: PermissionAction, what: string) {
super(`You do not have sufficient permissions to ${action.toLowerCase()} ${what}.`);
constructor(action: PermissionAction, what: string, why: string) {
super(`You do not have sufficient permissions to ${action.toLowerCase()} ${what} (${why}).`);
}
}
25 changes: 18 additions & 7 deletions src/permissions/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const applyPermissions = (
}

if (permissionStack === false) {
console.error(`No applicable permissions exist for ${ctx.user.role} ${type} ${action}.`);
// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
query.where(false);
return permissionStack;
Expand Down Expand Up @@ -93,13 +94,23 @@ export const getEntityToMutate = async (
let entity = await query.clone();

if (!entity) {
throw new NotFoundError('Entity to mutate');
console.error(
`Not found: ${Object.entries(where)
.map(([key, value]) => `${key}: ${value}`)
.join(', ')}`
);
throw new NotFoundError(`Entity to ${action.toLowerCase()}`);
}

applyPermissions(ctx, model.name, model.name, query, action);
entity = await query;
if (!entity) {
throw new PermissionError(action, `this ${model.name}`);
console.error(
`Permission error: ${Object.entries(where)
.map(([key, value]) => `${key}: ${value}`)
.join(', ')}`
);
throw new PermissionError(action, `this ${model.name}`, 'no available permissions applied');
}

return entity;
Expand All @@ -120,7 +131,7 @@ export const checkCanWrite = async (
return;
}
if (permissionStack === false) {
throw new PermissionError(action, getModelPlural(model));
throw new PermissionError(action, getModelPlural(model), 'no applicable permissions');
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- using `select(1 as any)` to instantiate an "empty" query builder
Expand All @@ -138,7 +149,7 @@ export const checkCanWrite = async (

const fieldPermissions = field[action === 'CREATE' ? 'creatableBy' : 'updatableBy'];
if (fieldPermissions && !fieldPermissions.includes(ctx.user.role)) {
throw new PermissionError(action, `this ${model.name}'s ${field.name}`);
throw new PermissionError(action, `this ${model.name}'s ${field.name}`, 'field permission not available');
}

linked = true;
Expand All @@ -153,7 +164,7 @@ export const checkCanWrite = async (
}

if (fieldPermissionStack === false || !fieldPermissionStack.length) {
throw new PermissionError(action, `this ${model.name}'s ${field.name}`);
throw new PermissionError(action, `this ${model.name}'s ${field.name}`, 'no applicable permissions on data to link');
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
Expand All @@ -168,10 +179,10 @@ export const checkCanWrite = async (
if (linked) {
const canMutate = await query;
if (!canMutate) {
throw new PermissionError(action, `this ${model.name} because there are no entities you can link it to`);
throw new PermissionError(action, `this ${model.name}`, 'no linkable entities');
}
} else if (action === 'CREATE') {
throw new PermissionError(action, `this ${model.name} because there are no entity types you can link it to`);
throw new PermissionError(action, `this ${model.name}`, 'no linkable entities');
}
};

Expand Down
7 changes: 6 additions & 1 deletion src/resolvers/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const resolve = async (ctx: FullContext, id?: string) => {
}

if (!res[0]) {
console.error('Entity not found', query.toString());
throw new NotFoundError('Entity not found');
}

Expand Down Expand Up @@ -119,7 +120,11 @@ const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins: Joins
}

if (field.queriableBy && !field.queriableBy.includes(node.ctx.user.role)) {
throw new PermissionError('READ', `${node.model.name}'s field "${field.name}"`);
throw new PermissionError(
'READ',
`${node.model.name}'s field "${field.name}"`,
'field permission not available'
);
}

return true;
Expand Down
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const summonByKey = <T>(array: readonly T[] | undefined, key: string, val

export const summon = <T>(array: readonly T[] | undefined, cb: Parameters<T[]['find']>[1], errorMessage?: string) => {
if (array === undefined) {
console.trace();
throw new Error('Base array is not defined.');
}
const result = array.find(cb);
Expand Down

0 comments on commit a4097e6

Please sign in to comment.