mirror of
https://github.com/datahub-project/datahub.git
synced 2025-08-11 10:46:52 +00:00

* Releases updated version of datahub-web client UI code * Fix typo in yarn lock * Change yarn lock to match yarn registry directories * Previous commit missed some paths * Even more changes to yarnlock missing in previous commit * Include codegen file for typings * Add files to get parity for datahub-web and current OS datahub-midtier * Add in typo fix from previous commit - change to proper license * Implement proper OS fix for person entity picture url * Workarounds for open source DH issues * Fixes institutional memory api and removes unopensourced tabs for datasets * Fixes search dataset deprecation and user search issue as a result of changes * Remove internal only options in the avatar menu
376 lines
8.8 KiB
TypeScript
376 lines
8.8 KiB
TypeScript
import { dasherize } from '@ember/string';
|
|
import { IGraphOptions } from '@datahub/shared/types/graph/graph-options';
|
|
|
|
type Node = Com.Linkedin.Metadata.Graph.Node;
|
|
type Graph = Com.Linkedin.Metadata.Graph.Graph;
|
|
type Edge = Com.Linkedin.Metadata.Graph.Edge;
|
|
type Attribute = Com.Linkedin.Metadata.Graph.Attribute;
|
|
|
|
/**
|
|
* Bag of all attributes possible in GraphViz
|
|
*
|
|
* https://graphviz.gitlab.io/_pages/doc/info/shapes.html
|
|
*/
|
|
interface IAllAtributes {
|
|
port?: string;
|
|
colspan?: string;
|
|
border?: string;
|
|
align?: 'CENTER' | 'LEFT' | 'RIGHT' | 'TEXT';
|
|
sides?: string; // LTBR (Left Top Bottom Right)
|
|
bgcolor?: string;
|
|
color?: string;
|
|
cellboarder?: string;
|
|
cellpadding?: string;
|
|
cellspacing?: string;
|
|
face?: string;
|
|
pointSize?: string;
|
|
// Graphviz won't render id without having href, post process required
|
|
id?: string;
|
|
href?: string;
|
|
}
|
|
|
|
/**
|
|
* A graphViz pseudo html node
|
|
*
|
|
* https://graphviz.gitlab.io/_pages/doc/info/shapes.html
|
|
*/
|
|
interface IVizHtmlNode {
|
|
type: string;
|
|
children?: Array<IVizHtmlNode>;
|
|
text?: string;
|
|
attributes?: IAllAtributes;
|
|
}
|
|
|
|
/**
|
|
* Options for font element
|
|
*/
|
|
type IFontOptions = Pick<IAllAtributes, 'color' | 'face' | 'pointSize'>;
|
|
|
|
/**
|
|
* FontElement pseudo html node
|
|
*/
|
|
interface IFont extends IVizHtmlNode {
|
|
type: 'FONT';
|
|
children: Array<IVizHtmlNode>;
|
|
attributes: IFontOptions;
|
|
}
|
|
|
|
/**
|
|
* Options for cell element
|
|
*/
|
|
type ICellOptions = Pick<
|
|
IAllAtributes,
|
|
| 'port'
|
|
| 'colspan'
|
|
| 'border'
|
|
| 'align'
|
|
| 'sides'
|
|
| 'bgcolor'
|
|
| 'color'
|
|
| 'cellpadding'
|
|
| 'cellspacing'
|
|
| 'id'
|
|
| 'href'
|
|
>;
|
|
|
|
/**
|
|
* CellElement pseudo html node
|
|
*/
|
|
interface ICell extends IVizHtmlNode {
|
|
type: 'TD';
|
|
children: Array<IVizHtmlNode>;
|
|
attributes: ICellOptions;
|
|
}
|
|
|
|
/**
|
|
* RowElement pseudo html node
|
|
*/
|
|
interface IRow extends IVizHtmlNode {
|
|
type: 'TR';
|
|
children: Array<IVizHtmlNode>;
|
|
}
|
|
|
|
/**
|
|
* Options available for a table
|
|
*/
|
|
type ITableOptions = Pick<
|
|
IAllAtributes,
|
|
| 'port'
|
|
| 'colspan'
|
|
| 'border'
|
|
| 'align'
|
|
| 'sides'
|
|
| 'bgcolor'
|
|
| 'color'
|
|
| 'cellboarder'
|
|
| 'cellpadding'
|
|
| 'cellspacing'
|
|
| 'id'
|
|
| 'href'
|
|
>;
|
|
|
|
/**
|
|
* TableElement pseudo node
|
|
*/
|
|
interface ITable extends IVizHtmlNode {
|
|
type: 'TABLE';
|
|
children: Array<IVizHtmlNode>;
|
|
attributes?: ITableOptions;
|
|
}
|
|
|
|
/**
|
|
* Text special node that is a string
|
|
*/
|
|
interface IText extends IVizHtmlNode {
|
|
type: 'text';
|
|
text: string;
|
|
}
|
|
|
|
/**
|
|
* Will transform a bag of options into attributes of a pseudo html element
|
|
* for example: CELLSPACING="0" CELLPADDING="1"
|
|
* @param options
|
|
*/
|
|
const domAttributesToString = (options: IAllAtributes): string =>
|
|
Object.keys(options)
|
|
.map((key: keyof IAllAtributes): string => {
|
|
const value = options[key];
|
|
const keyUppercased = dasherize(key).toUpperCase();
|
|
return `${keyUppercased}="${value}"`;
|
|
})
|
|
.join(' ');
|
|
|
|
/**
|
|
* Will render a node. It will also render the children.
|
|
* @param nodes
|
|
*/
|
|
const render = (nodes: Array<IVizHtmlNode | undefined>): string =>
|
|
(nodes.filter(Boolean) as Array<IVizHtmlNode>)
|
|
.map(({ type, children, text, attributes }): string | undefined =>
|
|
children ? `<${type} ${attributes ? domAttributesToString(attributes) : ''}>${render(children)}</${type}>` : text
|
|
)
|
|
.join('');
|
|
|
|
/**
|
|
* Text element constructor
|
|
* @param text
|
|
*/
|
|
const text = (text: string): IText => ({ type: 'text', text });
|
|
|
|
/**
|
|
* Font element constructor
|
|
* @param child
|
|
* @param attributes
|
|
*/
|
|
const font = (child: IVizHtmlNode, attributes: IFontOptions): IFont => ({
|
|
type: 'FONT',
|
|
attributes,
|
|
children: [child]
|
|
});
|
|
|
|
/**
|
|
* Cell element constructor
|
|
* @param child
|
|
* @param attributes
|
|
*/
|
|
const cell = (child: IVizHtmlNode, attributes: ICellOptions): ICell => ({
|
|
type: 'TD',
|
|
attributes,
|
|
children: [child]
|
|
});
|
|
|
|
/**
|
|
* Row element constructor
|
|
* @param children
|
|
*/
|
|
const row = (children: Array<ICell>): IRow => ({ type: 'TR', children });
|
|
|
|
/**
|
|
* Table element constructor
|
|
* @param children
|
|
* @param attributes
|
|
*/
|
|
const table = (children: Array<IRow>, attributes?: ITableOptions): ITable => ({ type: 'TABLE', children, attributes });
|
|
|
|
/**
|
|
* Will create a row for an attribute
|
|
* @param attribute
|
|
*/
|
|
const attributeToRow = (attribute: Attribute & { value?: string }): IRow =>
|
|
row([
|
|
cell(
|
|
table(
|
|
[
|
|
row([
|
|
cell(text(attribute.name), {
|
|
align: 'LEFT'
|
|
}),
|
|
cell(text(attribute.type || attribute.value || ''), {
|
|
align: 'RIGHT'
|
|
})
|
|
])
|
|
],
|
|
{
|
|
cellspacing: '0',
|
|
border: '0',
|
|
cellpadding: '10'
|
|
}
|
|
),
|
|
{
|
|
port: attribute.name,
|
|
id: `ENTITY-ATTRIBUTE::${attribute.name}${attribute.reference ? `::${attribute.reference}` : ''}`,
|
|
href: '-',
|
|
bgcolor: 'red'
|
|
}
|
|
)
|
|
]);
|
|
|
|
/**
|
|
* Will create an array of rows for a list of attributes
|
|
* @param attributes
|
|
*/
|
|
const attributesToRows = (attributes: Array<Attribute>, _options?: IGraphOptions): Array<IRow> =>
|
|
attributes.map(attributeToRow);
|
|
|
|
/**
|
|
* Will generate a toolbar for the node
|
|
* @param node node to create the actions for
|
|
* @param _options possible options needed
|
|
*/
|
|
const renderNodeToolbar = (node: Node, _options?: IGraphOptions): IVizHtmlNode =>
|
|
table(
|
|
[
|
|
row([
|
|
cell(text('View dataset detail <I>$1</I>'), {
|
|
id: `ENTITY-ACTION-GO-TO-ENTITY::${node.id}`,
|
|
href: '-',
|
|
align: 'RIGHT'
|
|
})
|
|
])
|
|
],
|
|
{
|
|
cellspacing: '0',
|
|
border: '0',
|
|
cellpadding: '0'
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Will render a node label using pseudo HTML elements
|
|
* @param node
|
|
*/
|
|
const renderNodeLabel = (node: Node, options?: IGraphOptions): string =>
|
|
`<${render([
|
|
// Root table that will serve as a margin for arrow connection
|
|
table(
|
|
[
|
|
row([
|
|
cell(
|
|
// Table that will contain title and attribtues
|
|
table(
|
|
[
|
|
row([
|
|
// Title of the entity
|
|
cell(font(text(node.displayName || node.id), { pointSize: '20' }), {
|
|
align: 'LEFT',
|
|
id: `ENTITY-TITLE::${node.id}`,
|
|
href: '-',
|
|
cellpadding: '10',
|
|
cellspacing: '0',
|
|
bgcolor: 'red',
|
|
port: 'root'
|
|
})
|
|
]),
|
|
// Append the attributes of the entity
|
|
...(node.attributes ? attributesToRows(node.attributes, options) : []),
|
|
...(node.entityUrn
|
|
? [
|
|
row([
|
|
cell(renderNodeToolbar(node, options), {
|
|
align: 'LEFT',
|
|
cellpadding: '10',
|
|
cellspacing: '0',
|
|
bgcolor: 'red',
|
|
id: `ENTITY-ACTIONS::${node.id}`,
|
|
href: '-'
|
|
})
|
|
])
|
|
]
|
|
: [])
|
|
],
|
|
{
|
|
cellspacing: '0',
|
|
border: '0',
|
|
cellpadding: '0'
|
|
}
|
|
),
|
|
{}
|
|
)
|
|
])
|
|
],
|
|
{ cellspacing: '10', border: '0' } // for arrow separation
|
|
)
|
|
])}>`;
|
|
|
|
/**
|
|
* Will return a dot notation node
|
|
* @param node
|
|
*/
|
|
const renderNode = (node: Node, options?: IGraphOptions): string =>
|
|
`"${node.id}" [
|
|
id = "ENTITY::${node.id}"
|
|
label = ${renderNodeLabel(node, options)}
|
|
]`;
|
|
|
|
/**
|
|
* Will return a dot notation edges
|
|
* @param node
|
|
*/
|
|
const renderEdges = (edges: Array<Edge>, _options?: IGraphOptions): Array<string> =>
|
|
edges.map((edge): string => {
|
|
const fromPort = edge.fromAttribute || 'root';
|
|
const from = `"${edge.fromNode}":${fromPort}`;
|
|
const to = `"${edge.toNode}":root`;
|
|
const label = edge.attributes?.map(attr => `${attr.name}:${attr.value}`).join(' ');
|
|
|
|
return `${from} -> ${to} [
|
|
id = "EDGE::${edge.fromNode}::${fromPort}::${edge.toNode}"
|
|
${label ? `label = "${label}"` : ''}
|
|
]`;
|
|
});
|
|
|
|
/**
|
|
* Will render all nodes
|
|
* @param nodes
|
|
*/
|
|
const renderNodes = (nodes: Array<Node>, options?: IGraphOptions): Array<string> =>
|
|
nodes.map(node => renderNode(node, options));
|
|
|
|
/**
|
|
* Will render a graph into a dot notation
|
|
*
|
|
* https://graphviz.gitlab.io/_pages/doc/info/lang.html
|
|
*
|
|
* @param graph
|
|
*/
|
|
export function graphToDot(graph: Graph, options?: IGraphOptions): string {
|
|
const dot = `
|
|
digraph {
|
|
graph [
|
|
rankdir = "LR"
|
|
bgcolor = "none"
|
|
];
|
|
node [
|
|
fontsize = "16"
|
|
fontname = "helvetica, open-sans"
|
|
shape = "none"
|
|
margin = "0"
|
|
];
|
|
edge [];
|
|
ranksep = 2
|
|
${renderNodes(graph.nodes, options).join('\n')}
|
|
${graph.edges ? renderEdges(graph.edges, options).join('\n') : ''}
|
|
}`;
|
|
return dot;
|
|
}
|