graphile-build-pg
Version:
Build a GraphQL schema by reflection over a PostgreSQL schema. Easy to customize since it's built with plugins on graphile-build
142 lines (132 loc) • 3.72 kB
Flow
// @flow
import type {
PgProc,
PgClass,
PgAttribute,
PgConstraint,
} from "./plugins/PgIntrospectionPlugin";
/*
* Please only use capitals for aliases and lower case for the values.
*/
export const CREATE = "create";
export const READ = "read";
export const UPDATE = "update";
export const DELETE = "delete";
export const FILTER = "filter";
export const ORDER = "order";
export const ALL = "all";
export const MANY = "many";
export const EXECUTE = "execute";
export const BASE = "base";
const aliases = {
C: CREATE,
R: READ,
U: UPDATE,
D: DELETE,
F: FILTER,
O: ORDER,
A: ALL,
M: MANY,
X: EXECUTE,
B: BASE,
};
const PERMISSIONS_THAT_REQUIRE_READ = [UPDATE, CREATE, DELETE, ALL, MANY];
function parse(arrOrNot, errorPrefix = "Error") {
if (!arrOrNot) {
return null;
}
const arr = Array.isArray(arrOrNot) ? arrOrNot : [arrOrNot];
let all = false;
const arrayNormalized = [].concat(
...arr.map(str => {
if (str === true || str === "*") {
all = true;
return [];
}
if (str[0] === ":") {
const perms = str
.slice(1)
.split("")
.map(p => aliases[p]);
const bad = perms.find(p => !p);
if (bad) {
throw new Error(
`${errorPrefix} - abbreviated parameter '${bad}' not understood`
);
}
return perms;
} else {
const perms = str.split(",");
// TODO: warning if not in list?
return perms;
}
})
);
if (all) {
return true;
}
return arrayNormalized;
}
export default function omit(
entity: PgProc | PgClass | PgAttribute | PgConstraint,
permission: string
) {
const tags = entity.tags;
const omitSpecRaw = tags.omit;
// '@include' is not being released yet because it would mean every new
// filter we added would become a breaking change for people using @include.
const includeSpecRaw = null;
// const includeSpecRaw = tags.include;
if (omitSpecRaw && includeSpecRaw) {
throw new Error(
`Error when processing instructions for ${entity.kind} '${entity.name}' - you must only specify @omit or @include, not both`
);
}
const omitSpec = parse(
omitSpecRaw,
`Error when processing @omit instructions for ${entity.kind} '${entity.name}'`
);
const includeSpec = parse(
includeSpecRaw,
`Error when processing @include instructions for ${entity.kind} '${entity.name}'`
);
if (omitSpec) {
if (omitSpec === true) {
return true;
}
if (omitSpec.indexOf(READ) >= 0) {
const bad = PERMISSIONS_THAT_REQUIRE_READ.filter(
p => omitSpec.indexOf(p) === -1
);
if (bad.length > 0) {
throw new Error(
`Processing @omit for ${entity.kind} '${entity.name}' - '${bad.join(
","
)}' must be omitted when '${READ}' is omitted. Add '${bad.join(
","
)}' to the @omit clause, or use '@omit' to omit all actions.`
);
}
}
return omitSpec.indexOf(permission) >= 0;
} else if (includeSpec) {
if (includeSpec === true) {
throw new Error(
`Error when processing instructions for ${entity.kind} '${entity.name}' - @include should specify a list of actions`
);
}
if (includeSpec.indexOf(READ) === -1) {
const bad = PERMISSIONS_THAT_REQUIRE_READ.find(
p => includeSpec.indexOf(p) >= 0
);
if (bad) {
throw new Error(
`Error when processing @include for ${entity.kind} '${entity.name}' - we currently don't support '${bad}' when '${READ}' is forbidden`
);
}
}
return includeSpec.indexOf(permission) === -1;
} else {
return false;
}
}