@cerbos/http
Version:
Client library for interacting with the Cerbos policy decision point service over HTTP from browser-based applications
215 lines (196 loc) • 5.14 kB
text/typescript
import type {
DescMessage,
DescService,
MessageShape,
MessageValidType,
} from "@bufbuild/protobuf";
import { toJson, toJsonString } from "@bufbuild/protobuf";
import { stringify as queryStringify } from "qs";
import type { ListAuditLogEntriesRequestSchema } from "@cerbos/api/cerbos/request/v1/request_pb";
import {
ListAuditLogEntriesRequest_Kind,
ListAuditLogEntriesRequest_KindSchema,
} from "@cerbos/api/cerbos/request/v1/request_pb";
import {
CerbosAdminService as admin,
CerbosService as cerbos,
} from "@cerbos/api/cerbos/svc/v1/svc_pb";
import { Health as health } from "@cerbos/api/grpc/health/v1/health_pb";
import { methodName } from "@cerbos/core/~internal";
export interface RequestInitWithUrl extends RequestInit {
url: string;
}
type HttpMethod = "GET" | "POST" | "DELETE";
type Serialize<I extends DescMessage = DescMessage> = (
request: MessageValidType<I>,
init: RequestInitWithUrl,
) => RequestInitWithUrl;
interface DescEndpoint<I extends DescMessage> {
method: HttpMethod;
path: string;
serialize: (schema: I) => Serialize<I>;
}
type DescEndpoints<Service extends DescService> = {
[Method in keyof Service["method"]]:
| DescEndpoint<Service["method"][Method]["input"]>
| undefined;
};
interface Endpoint {
method: HttpMethod;
path: string;
serialize: Serialize;
}
export const endpoints = new Map<string, Endpoint>();
function defineEndpoints<Service extends DescService>(
service: Service,
descs: DescEndpoints<Service>,
): void {
for (const method of service.methods) {
const endpoint = descs[method.localName];
if (endpoint) {
endpoints.set(methodName(method), {
method: endpoint.method,
path: endpoint.path,
serialize: endpoint.serialize(method.input),
});
}
}
}
defineEndpoints(admin, {
addOrUpdatePolicy: {
method: "POST",
path: "/admin/policy",
serialize: serializeToBody,
},
addOrUpdateSchema: {
method: "POST",
path: "/admin/schema",
serialize: serializeToBody,
},
deletePolicy: {
method: "POST",
path: "/admin/policy/delete",
serialize: serializeToQueryString,
},
deleteSchema: {
method: "DELETE",
path: "/admin/schema",
serialize: serializeToQueryString,
},
disablePolicy: {
method: "POST",
path: "/admin/policy/disable",
serialize: serializeToQueryString,
},
enablePolicy: {
method: "POST",
path: "/admin/policy/enable",
serialize: serializeToQueryString,
},
getPolicy: {
method: "GET",
path: "/admin/policy",
serialize: serializeToQueryString,
},
getSchema: {
method: "GET",
path: "/admin/schema",
serialize: serializeToQueryString,
},
inspectPolicies: {
method: "GET",
path: "/admin/policies/inspect",
serialize: serializeToQueryString,
},
listAuditLogEntries: {
method: "GET",
path: "/admin/auditlog/list/",
serialize: serializeListAuditLogEntriesRequest,
},
listPolicies: {
method: "GET",
path: "/admin/policies",
serialize: serializeToQueryString,
},
listSchemas: {
method: "GET",
path: "/admin/schemas",
serialize: serializeToQueryString,
},
purgeStoreRevisions: {
method: "DELETE",
path: "/admin/store/revisions",
serialize: serializeToQueryString,
},
reloadStore: {
method: "GET",
path: "/admin/store/reload",
serialize: serializeToQueryString,
},
});
defineEndpoints(cerbos, {
checkResourceBatch: undefined,
checkResourceSet: undefined,
checkResources: {
method: "POST",
path: "/api/check/resources",
serialize: serializeToBody,
},
planResources: {
method: "POST",
path: "/api/plan/resources",
serialize: serializeToBody,
},
serverInfo: {
method: "GET",
path: "/api/server_info",
serialize: serializeToQueryString,
},
});
defineEndpoints(health, {
check: {
method: "GET",
path: "/_cerbos/health",
serialize: serializeToQueryString,
},
list: undefined,
watch: undefined,
});
function serializeToBody<I extends DescMessage>(schema: I): Serialize<I> {
return (request, init) => ({
...init,
body: toJsonString(schema, request as MessageShape<I>),
});
}
function serializeToQueryString<I extends DescMessage>(
schema: I,
): Serialize<I> {
return (request, { url, ...init }) => ({
...init,
url: `${url}?${toQueryString(schema, request)}`,
});
}
function serializeListAuditLogEntriesRequest(
schema: typeof ListAuditLogEntriesRequestSchema,
): Serialize<typeof ListAuditLogEntriesRequestSchema> {
return ({ kind, ...rest }, { url, ...init }) => {
const path = ListAuditLogEntriesRequest_KindSchema.value[kind].name;
const queryString = toQueryString(schema, {
kind: ListAuditLogEntriesRequest_Kind.UNSPECIFIED,
...rest,
});
return {
...init,
url: `${url}${path}?${queryString}`,
};
};
}
function toQueryString<I extends DescMessage>(
schema: I,
request: MessageValidType<I>,
): string {
return queryStringify(toJson(schema, request as MessageShape<I>), {
allowDots: true,
arrayFormat: "repeat",
});
}