forest-cli
Version:
The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.
160 lines • 15.2 kB
JavaScript
;
/**
* Rebuilds the patchable "layout" document from the serialized rendering.
*
* The server has no raw GET for the layout domain (GET /api/layout always
* returns [] — the renderings table has no `layout` column). The frontend
* reads the layout through GET /api/renderings/:project/:env/:team, a JSON:API
* document (snake_case attributes, resources split into `included`). This
* module inverts that serialization into the configuration shape the PATCH
* paths address (camelCase, see the server's collection validators/models).
*
* v1 maps the curated, patchable subset: collections base props, columns,
* fields, segments, actions, viewEdit.summaryView, and dashboards.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderingToCanonical = void 0;
/** `customers-email` -> `email` (resource ids are prefixed by the collection). */
function stripCollectionPrefix(resourceId, collectionId) {
const prefix = `${collectionId}-`;
return resourceId.startsWith(prefix) ? resourceId.slice(prefix.length) : resourceId;
}
function indexIncluded(doc) {
const index = new Map();
(doc.included ?? []).forEach(resource => index.set(`${resource.type}:${resource.id}`, resource));
return index;
}
function relatedIds(resource, relationship) {
const data = resource?.relationships?.[relationship]?.data;
if (Array.isArray(data))
return data;
if (data && typeof data === 'object')
return [data];
return [];
}
/** Resolve a relationship's references into the included resources they point to. */
function relatedResources(collection, index, relationship, includedType) {
return relatedIds(collection, relationship)
.map(ref => index.get(`${includedType}:${ref.id}`))
.filter((resource) => resource !== undefined);
}
/** Shallow snake_case -> camelCase for generic resources (segments, actions…). */
function camelizeShallow(attributes) {
return Object.fromEntries(Object.entries(attributes).map(([key, value]) => [
key.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
value,
]));
}
function mapColumns(collection, index) {
return relatedResources(collection, index, 'columns', 'columns')
.map(column => {
const attrs = column.attributes ?? {};
return {
id: stripCollectionPrefix(column.id, collection.id),
// Wire format exposes is_hidden; the patchable config uses isVisible.
isVisible: attrs.is_hidden !== true,
position: attrs.position ?? 0,
};
})
.sort((a, b) => a.position - b.position);
}
const FIELD_PATCHABLE_KEYS = [
['display_name', 'displayName'],
['description', 'description'],
['is_read_only', 'isReadOnly'],
['is_filter_displayed', 'isFilterDisplayed'],
['is_dissociate_displayed', 'isDissociateDisplayed'],
['widget_edit', 'widgetEdit'],
['widget_display', 'widgetDisplay'],
['mapping_values', 'mappingValues'],
['conditional_formatting', 'conditionalFormatting'],
];
function mapFields(collection, index) {
return relatedResources(collection, index, 'fields', 'fields')
.map(field => {
const attrs = field.attributes ?? {};
const mapped = {
id: attrs.field ?? stripCollectionPrefix(field.id, collection.id),
};
FIELD_PATCHABLE_KEYS.forEach(([wire, canonical]) => {
if (wire in attrs)
mapped[canonical] = attrs[wire];
});
return mapped;
})
.sort((a, b) => String(a.id).localeCompare(String(b.id)));
}
function mapGenericList(collection, index, relationship, includedType) {
return relatedResources(collection, index, relationship, includedType).map(resource => ({
id: resource.id,
...camelizeShallow(resource.attributes ?? {}),
}));
}
function mapCollection(collection, index) {
const attrs = collection.attributes ?? {};
const sortingFieldRef = relatedIds(collection, 'default_sorting_field')[0];
const viewEdit = index.get(`viewEdits:${collection.id}`);
return {
defaultSortingFieldName: sortingFieldRef
? stripCollectionPrefix(sortingFieldRef.id, collection.id)
: null,
defaultSortingOrder: attrs.default_sorting_field_order ?? null,
displayName: attrs.display_name ?? null,
displayNamePlural: attrs.display_name_plural ?? null,
icon: attrs.icon ?? null,
id: collection.id,
layout: {
actions: mapGenericList(collection, index, 'custom_actions', 'customActions'),
columns: mapColumns(collection, index),
fields: mapFields(collection, index),
segments: mapGenericList(collection, index, 'segments', 'segments'),
viewEdit: { summaryView: viewEdit?.attributes?.summary_view ?? null },
},
restrictedToSegments: attrs.restricted_to_segments === true,
};
}
/**
* Workspaces carry an ordered list of components; component `options` (tabs,
* filters, visible columns…) is deep and polymorphic, so it is kept opaque
* (round-tripped verbatim, set as a whole on add). Addressing is by the
* workspace/component `id` (the patch route's `:collectionId` param actually
* receives the workspace id).
*/
function mapWorkspace(workspace, index) {
const components = relatedResources(workspace, index, 'components', 'workspace-components').map(component => ({ id: component.id, ...camelizeShallow(component.attributes ?? {}) }));
return { id: workspace.id, ...camelizeShallow(workspace.attributes ?? {}), components };
}
/** Inboxes reference a collection (and, for segment inboxes, a segment) by id. */
function mapInbox(inbox) {
const collectionRef = relatedIds(inbox, 'collection')[0];
const segmentRef = relatedIds(inbox, 'segment')[0];
return {
id: inbox.id,
...camelizeShallow(inbox.attributes ?? {}),
...(collectionRef ? { collectionId: collectionRef.id } : {}),
...(segmentRef ? { segmentId: segmentRef.id } : {}),
};
}
/** Build the canonical patchable layout document from a rendering response. */
function renderingToCanonical(doc) {
const index = indexIncluded(doc);
const byType = (type) => (doc.included ?? []).filter(resource => resource.type === type);
const collections = byType('collections')
.map(collection => mapCollection(collection, index))
.sort((a, b) => a.id.localeCompare(b.id));
const dashboards = byType('dashboards').map(dashboard => ({
id: dashboard.id,
...camelizeShallow(dashboard.attributes ?? {}),
}));
const workspaces = byType('workspaces').map(workspace => mapWorkspace(workspace, index));
const inboxes = byType('inboxes').map(inbox => mapInbox(inbox));
return {
collections,
dashboards,
inboxes,
sections: doc.data.attributes?.sections ?? [],
workspaces,
};
}
exports.renderingToCanonical = renderingToCanonical;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyaW5nLW1hcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9sYXlvdXQvcmVuZGVyaW5nLW1hcHBlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7OztHQVlHOzs7QUE2Qkgsa0ZBQWtGO0FBQ2xGLFNBQVMscUJBQXFCLENBQUMsVUFBa0IsRUFBRSxZQUFvQjtJQUNyRSxNQUFNLE1BQU0sR0FBRyxHQUFHLFlBQVksR0FBRyxDQUFDO0lBRWxDLE9BQU8sVUFBVSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQztBQUN0RixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsR0FBb0I7SUFDekMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQTJCLENBQUM7SUFDakQsQ0FBQyxHQUFHLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxRQUFRLENBQUMsSUFBSSxJQUFJLFFBQVEsQ0FBQyxFQUFFLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBRWpHLE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUNqQixRQUFxQyxFQUNyQyxZQUFvQjtJQUVwQixNQUFNLElBQUksR0FBRyxRQUFRLEVBQUUsYUFBYSxFQUFFLENBQUMsWUFBWSxDQUFDLEVBQUUsSUFBZSxDQUFDO0lBRXRFLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFBRSxPQUFPLElBQTJDLENBQUM7SUFDNUUsSUFBSSxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUTtRQUFFLE9BQU8sQ0FBQyxJQUFvQyxDQUFDLENB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