forest-cli
Version:
The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.
171 lines • 15.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const context_1 = __importDefault(require("@forestadmin/context"));
const superagent_1 = __importDefault(require("superagent"));
const errors_1 = require("./errors");
/**
* Read/write access to a rendering's layout, folders and workflows documents.
*
* The "layout" is the server-side rendering. There is no raw GET for the
* layout domain (GET /api/layout returns []); the source of truth is the
* JSON:API rendering document, read by name through
* `GET /api/renderings/:project/:env/:team` — the same route the frontend uses.
*
* This adapter intentionally returns the raw response body (snake_case
* attributes, `included` graph) so the rendering mapper can invert the
* serialization itself. It reuses the toolbelt authenticator/env exactly like
* the other managers (TeamManager, EnvironmentManager) — no auth is
* re-implemented here. Later milestones (diff/apply) add the
* `PATCH /api/:domain` write path on top of the same headers.
*/
class LayoutManager {
constructor() {
const { assertPresent, authenticator, env } = context_1.default.inject();
assertPresent({ authenticator, env });
this.authenticator = authenticator;
this.serverUrl = env.FOREST_SERVER_URL;
}
/** GET /api/renderings/:project/:env/:team — the rendering as JSON:API. */
async getRendering(scope) {
const path = [scope.projectName, scope.environmentName, scope.teamName]
.map(segment => encodeURIComponent(segment))
.join('/');
try {
const response = await superagent_1.default
.get(`${this.serverUrl}/api/renderings/${path}`)
.set('Authorization', `Bearer ${this.authenticator.getAuthToken()}`)
.set('forest-project-id', String(scope.projectId))
.set('forest-environment-id', String(scope.environmentId))
.set('forest-team-id', String(scope.teamId))
.send();
return response.body;
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
}
/**
* GET /api/:domain/:project/:env/:team — the raw patchable document for a
* non-layout domain (folders, workflows). The layout domain has no usable raw
* GET (it returns []), so use {@link getRendering} for it instead.
*/
async getLayoutDomain(domain, scope) {
const path = [domain, scope.projectName, scope.environmentName, scope.teamName]
.map(segment => encodeURIComponent(segment))
.join('/');
try {
const response = await superagent_1.default
.get(`${this.serverUrl}/api/${path}`)
.set('Authorization', `Bearer ${this.authenticator.getAuthToken()}`)
.set('forest-project-id', String(scope.projectId))
.set('forest-environment-id', String(scope.environmentId))
.set('forest-team-id', String(scope.teamId))
.send();
return (response.body ?? []);
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
}
/**
* PATCH /api/:domain — apply an RFC 6902 JSON-Patch array, scoped by the
* environment/team headers. The patch is atomic server-side; a 204 is
* expected on success. Only `op`/`path`/`value` are sent (planner metadata
* is dropped). A no-op array is skipped entirely.
*/
async patchDomain(domain, ops, scope) {
if (ops.length === 0)
return;
const body = ops.map(({ op, path, value }) => value === undefined ? { op, path } : { op, path, value });
try {
await superagent_1.default
.patch(`${this.serverUrl}/api/${domain}`)
.set('Authorization', `Bearer ${this.authenticator.getAuthToken()}`)
.set('forest-project-id', String(scope.projectId))
.set('forest-environment-id', String(scope.environmentId))
.set('forest-team-id', String(scope.teamId))
.send(body);
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
}
/** The numeric rendering id for this scope (needed by the workflow presigned request). */
async getRenderingId(scope) {
const rendering = await this.getRendering(scope);
return Number(rendering.data.id);
}
/**
* Download the currently-stored BPMN of a workflow version, to diff against a
* freshly compiled one (idempotency). The server returns a presigned S3 GET
* URL; the bytes are then fetched straight from S3 (no Forest auth there).
*/
async getWorkflowBpmn(scope, workflowId, collectionId, version, renderingId) {
try {
const meta = await superagent_1.default
.get(`${this.serverUrl}/api/workflows/${workflowId}/bpmn`)
.query({ collectionId, version })
.set('Authorization', `Bearer ${this.authenticator.getAuthToken()}`)
.set('forest-project-id', String(scope.projectId))
.set('forest-environment-id', String(scope.environmentId))
.set('forest-team-id', String(scope.teamId))
.set('forest-rendering-id', String(renderingId));
const { signedUrl } = meta.body;
if (!signedUrl)
return '';
const file = await superagent_1.default.get(signedUrl).buffer(true);
return file.text ?? '';
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
}
/**
* Upload a workflow's compiled BPMN and return its S3 version id (to store in
* `bpmnAwsS3Identifier`). Mirrors the Forest UI exactly: ask the server for a
* presigned S3 POST, multipart-upload the BPMN to S3, read `x-amz-version-id`.
*/
async uploadWorkflowBpmn(scope, workflowId, collectionId, renderingId, bpmn) {
const size = Buffer.byteLength(bpmn);
let presigned;
try {
const response = await superagent_1.default
.post(`${this.serverUrl}/api/workflows/${workflowId}/generate-presigned-request`)
.query({ collectionId })
.set('Authorization', `Bearer ${this.authenticator.getAuthToken()}`)
.set('forest-project-id', String(scope.projectId))
.set('forest-environment-id', String(scope.environmentId))
.set('forest-team-id', String(scope.teamId))
.set('forest-rendering-id', String(renderingId))
.send({ name: `${workflowId}.bpmn`, size, type: 'text/xml' });
presigned = response.body;
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
// Presigned S3 POST: send the returned policy fields, then the file last. No
// Forest auth here — the presigned signature authorizes the upload.
let response;
try {
const upload = superagent_1.default.post(presigned.url);
Object.entries(presigned.fields).forEach(([key, value]) => upload.field(key, value));
response = await upload.attach('file', Buffer.from(bpmn), {
contentType: 'text/xml',
filename: `${workflowId}.bpmn`,
});
}
catch (error) {
throw (0, errors_1.toLayoutApiError)(error);
}
const versionId = response.headers['x-amz-version-id'];
if (!versionId) {
throw new Error('S3 did not return a version id for the uploaded BPMN (bucket versioning off?).');
}
return versionId;
}
}
exports.default = LayoutManager;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF5b3V0LW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmljZXMvbGF5b3V0L2xheW91dC1tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBRUEsbUVBQTJDO0FBQzNDLDREQUErQjtBQUUvQixxQ0FBNEM7QUFFNUM7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFxQixhQUFhO0lBS2hDO1FBQ0UsTUFBTSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsR0FBRyxFQUFFLEdBQUcsaUJBQU8sQ0FBQyxNQUFNLEVBSTNELENBQUM7UUFDRixhQUFhLENBQUMsRUFBRSxhQUFhLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUV0QyxJQUFJLENBQUMsYUFBYSxHQUFHLGFBQWEsQ0FBQztRQUNuQyxJQUFJLENBQUMsU0FBUyxHQUFHLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQztJQUN6QyxDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLEtBQUssQ0FBQyxZQUFZLENBQUMsS0FBa0I7UUFDbkMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxlQUFlLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQzthQUNwRSxHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsQ0FBQzthQUMzQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFYixJQUFJO1lBQ0YsTUFBTSxRQUFRLEdBQUcsTUFBTSxvQkFBSztpQkFDekIsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLFNBQVMsbUJBQW1CLElBQUksRUFBRSxDQUFDO2lCQUMvQyxHQUFHLENBQUMsZUFBZSxFQUFFLFVBQVUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDO2lCQUNuRSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxTQUFT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