@cerbos/http
Version:
Client library for interacting with the Cerbos policy decision point service over HTTP from browser-based applications
247 lines • 9.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transport = void 0;
exports.eachLine = eachLine;
const qs_1 = require("qs");
const core_1 = require("@cerbos/core");
const request_1 = require("./protobuf/cerbos/request/v1/request");
const response_1 = require("./protobuf/cerbos/response/v1/response");
const health_1 = require("./protobuf/grpc/health/v1/health");
const services = {
admin: {
addOrUpdatePolicy: {
method: "POST",
path: "/admin/policy",
requestType: request_1.AddOrUpdatePolicyRequest,
responseType: response_1.AddOrUpdatePolicyResponse,
serializeRequest: serializeRequestToBody,
},
addOrUpdateSchema: {
method: "POST",
path: "/admin/schema",
requestType: request_1.AddOrUpdateSchemaRequest,
responseType: response_1.AddOrUpdateSchemaResponse,
serializeRequest: serializeRequestToBody,
},
deleteSchema: {
method: "DELETE",
path: "/admin/schema",
requestType: request_1.DeleteSchemaRequest,
responseType: response_1.DeleteSchemaResponse,
serializeRequest: serializeRequestToQueryString,
},
disablePolicy: {
method: "DELETE",
path: "/admin/policy",
requestType: request_1.DisablePolicyRequest,
responseType: response_1.DisablePolicyResponse,
serializeRequest: serializeRequestToQueryString,
},
enablePolicy: {
method: "POST",
path: "/admin/policy/enable",
requestType: request_1.EnablePolicyRequest,
responseType: response_1.EnablePolicyResponse,
serializeRequest: serializeRequestToQueryString,
},
getPolicy: {
method: "GET",
path: "/admin/policy",
requestType: request_1.GetPolicyRequest,
responseType: response_1.GetPolicyResponse,
serializeRequest: serializeRequestToQueryString,
},
getSchema: {
method: "GET",
path: "/admin/schema",
requestType: request_1.GetSchemaRequest,
responseType: response_1.GetSchemaResponse,
serializeRequest: serializeRequestToQueryString,
},
inspectPolicies: {
method: "GET",
path: "/admin/policies/inspect",
requestType: request_1.InspectPoliciesRequest,
responseType: response_1.InspectPoliciesResponse,
serializeRequest: serializeRequestToQueryString,
},
listAuditLogEntries: {
method: "GET",
path: "/admin/auditlog/list/",
requestType: request_1.ListAuditLogEntriesRequest,
responseType: response_1.ListAuditLogEntriesResponse,
serializeRequest: serializeListAuditLogEntriesRequest,
},
listPolicies: {
method: "GET",
path: "/admin/policies",
requestType: request_1.ListPoliciesRequest,
responseType: response_1.ListPoliciesResponse,
serializeRequest: serializeRequestToQueryString,
},
listSchemas: {
method: "GET",
path: "/admin/schemas",
requestType: request_1.ListSchemasRequest,
responseType: response_1.ListSchemasResponse,
serializeRequest: serializeRequestToQueryString,
},
reloadStore: {
method: "GET",
path: "/admin/store/reload",
requestType: request_1.ReloadStoreRequest,
responseType: response_1.ReloadStoreResponse,
serializeRequest: serializeRequestToQueryString,
},
},
cerbos: {
checkResources: {
method: "POST",
path: "/api/check/resources",
requestType: request_1.CheckResourcesRequest,
responseType: response_1.CheckResourcesResponse,
serializeRequest: serializeRequestToBody,
},
planResources: {
method: "POST",
path: "/api/plan/resources",
requestType: request_1.PlanResourcesRequest,
responseType: response_1.PlanResourcesResponse,
serializeRequest: serializeRequestToBody,
},
serverInfo: {
method: "GET",
path: "/api/server_info",
requestType: request_1.ServerInfoRequest,
responseType: response_1.ServerInfoResponse,
serializeRequest: serializeRequestToQueryString,
},
},
health: {
check: {
method: "GET",
path: "/_cerbos/health",
requestType: health_1.HealthCheckRequest,
responseType: health_1.HealthCheckResponse,
serializeRequest: serializeRequestToQueryString,
},
},
};
class Transport {
baseUrl;
userAgent;
constructor(baseUrl, userAgent) {
this.baseUrl = baseUrl;
this.userAgent = userAgent;
}
async unary(service, method, request, headers, abortHandler) {
const { response, responseType } = await this.fetch(service, method, request, headers, abortHandler);
if (!response.ok) {
throw core_1.NotOK.fromJSON(await response.text());
}
return responseType.fromJSON(await response.json());
}
async *serverStream(service, method, request, headers, abortHandler) {
const { response, responseType } = await this.fetch(service, method, request, headers, abortHandler);
try {
if (!response.body) {
throw new Error("Missing response body");
}
for await (const line of eachLine(response.body)) {
const message = JSON.parse(line);
if (!(0, core_1._isObject)(message)) {
throw new Error(`Unexpected message: wanted object, got ${line}`);
}
const { result, error } = message;
if (error) {
throw core_1.NotOK.fromJSON(JSON.stringify(error));
}
if (!result) {
throw new Error(`Missing result in ${line}`);
}
yield responseType.fromJSON(result);
}
}
catch (error) {
response.body?.cancel().catch(() => {
// ignore failure to cancel
});
abortHandler.throwIfAborted();
if (error instanceof core_1.NotOK) {
throw error;
}
throw new core_1.NotOK(core_1.Status.INTERNAL, error instanceof Error
? `Invalid stream: ${error.message}`
: "Invalid stream", { cause: error });
}
}
async fetch(service, method, request, headers, abortHandler) {
const { method: requestMethod, path, requestType, responseType, serializeRequest, } = services[service][method]; // https://github.com/microsoft/TypeScript/issues/30581
headers.set("User-Agent", this.userAgent);
const init = {
url: this.baseUrl + path,
method: requestMethod,
headers,
};
if (abortHandler.signal) {
init.signal = abortHandler.signal;
}
const { url, ...rest } = serializeRequest(request, requestType, init);
try {
return {
response: await fetch(url, rest),
responseType,
};
}
catch (error) {
abortHandler.throwIfAborted();
throw new core_1.NotOK(core_1.Status.UNKNOWN, error instanceof Error
? `Request failed: ${error.message}`
: "Request failed", { cause: error });
}
}
}
exports.Transport = Transport;
function serializeRequestToBody(request, requestType, init) {
return {
...init,
body: JSON.stringify(requestType.toJSON(request)),
};
}
function serializeRequestToQueryString(request, requestType, { url, ...init }) {
return {
...init,
url: `${url}?${queryStringifyRequest(requestType, request)}`,
};
}
function serializeListAuditLogEntriesRequest({ kind, ...rest }, requestType, { url, ...init }) {
return {
...init,
url: `${url}${request_1.ListAuditLogEntriesRequest_Kind[kind]}?${queryStringifyRequest(requestType, { kind: 0, ...rest })}`,
};
}
function queryStringifyRequest(requestType, request) {
return (0, qs_1.stringify)(requestType.toJSON(request), {
allowDots: true,
arrayFormat: "repeat",
});
}
async function* eachLine(stream) {
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
let buffer = "";
let start = 0;
for await (const chunk of stream) {
buffer += utf8Decoder.decode(chunk, { stream: true });
let end;
while ((end = buffer.indexOf("\n", start)) >= 0) {
yield buffer.slice(start, end);
start = end + 1;
}
buffer = buffer.slice(start);
start = 0;
}
if (buffer.length > 0) {
yield buffer;
}
}
//# sourceMappingURL=transport.js.map