Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"chai": "^4",
"globby": "^8",
"mocha": "^5",
"node-plantuml": "^0.9.0",
"nyc": "^14",
"ts-node": "^8",
"tslint": "^5"
Expand Down
13 changes: 12 additions & 1 deletion src/shared/contenttypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ interface ObjectFieldContent {
additionalInfo: string;
}

interface PumlEntity {
name: string;
fields: Array<PumlField>
}

interface PumlField {
name: string,
type: string,
typeAdditionalInfo?: string
}

interface TriggersContent {
counter: number;
groups: Array<TriggerGroupContent>;
Expand Down Expand Up @@ -79,4 +90,4 @@ interface TriggerDuplicate {
triggers: string;
}

export {IndexContent, ContentLink, ObjectsContent, ObjectGroupContent, ObjectContent, ObjectFieldContent, TriggersContent, TriggerGroupContent, TriggerContent, TriggerDuplicate}
export {IndexContent, ContentLink, ObjectsContent, ObjectGroupContent, ObjectContent, ObjectFieldContent, PumlEntity, PumlField, TriggersContent, TriggerGroupContent, TriggerContent, TriggerDuplicate}
107 changes: 86 additions & 21 deletions src/shared/processors/object/objects.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { join } from 'path';
import { lstatSync, writeFileSync } from 'fs';
import { createWriteStream, lstatSync, writeFileSync } from 'fs';
import { HTMLGenerator } from '../../htmlGenerator';
import { createDirectory, parseXMLToJS, getDirectoryEntries } from '../../files';
import { ContentLink, ObjectsContent, ObjectGroupContent, ObjectContent, ObjectFieldContent } from '../../contenttypes';
import { ContentLink, ObjectsContent, ObjectGroupContent, ObjectContent, ObjectFieldContent, PumlField, PumlEntity } from '../../contenttypes';
import { Metadata, MetadataGroup, DocumentorConfig } from '../../configtypes';
import { enrichField, addAdditionalFieldInfo } from './objectutils';
var plantuml = require('node-plantuml');

class ObjectProcessor {
groups : Map<string, MetadataGroup>;
Expand All @@ -16,11 +17,11 @@ class ObjectProcessor {
indexFile : string;
generator : HTMLGenerator;
groupFile : string;
content: ObjectsContent;
content: ObjectsContent;
missingDescriptions: boolean;

constructor(config, sourceDir, outputDir, generator) {

this.config=config;
this.missingDescriptions=false;
this.outputDir=join(outputDir, 'objects');
Expand All @@ -34,7 +35,7 @@ class ObjectProcessor {
this.sourceDir=sourceDir+this.mdSetup.subdirectory;

this.indexFile=join(this.outputDir, '/objects.html');


this.content={groups: [],
missingDescriptions: [],
Expand All @@ -50,7 +51,7 @@ class ObjectProcessor {

this.generator.generateHTML(join('objects', 'objects.ejs'), this.content)
.then(html => {
writeFileSync(this.indexFile, html);
writeFileSync(this.indexFile, html);
});

this.content.groups.forEach(group => {
Expand All @@ -59,13 +60,15 @@ class ObjectProcessor {
this.groupFile=join(this.outputDir, group.name+'.html');
writeFileSync(this.groupFile, html);
});

this.createERD(group);
});

let objectLink: ContentLink = {
title: 'Objects',
href: 'objects/objects.html',
image: this.mdSetup.image,
description: this.mdSetup.description,
description: this.mdSetup.description,
warning: this.missingDescriptions,
error: false
};
Expand Down Expand Up @@ -98,14 +101,14 @@ class ObjectProcessor {
for (let member of this.mdSetup.members) {
if ( ((group.objects) && (group.objects.includes(member.name))) ||
((typeof group.prefix !=='undefined') && member.name.startsWith(group.prefix)) ||
((typeof group.additional !== 'undefined') && (group.additional.includes(member)))
)
((typeof group.additional !== 'undefined') && (group.additional.includes(member)))
)
{
let groupMember={member: member,
group: group};
group.members.push(groupMember);
}
}
}
}
}
}
Expand All @@ -122,10 +125,10 @@ class ObjectProcessor {

processGroup(group : MetadataGroup) {
group.started=true;
let contentGroup : ObjectGroupContent={title: group.title,
name: group.name,
description: group.description,
link: group.name + '.html',
let contentGroup : ObjectGroupContent={title: group.title,
name: group.name,
description: group.description,
link: group.name + '.html',
objects : [],
menuItems : []};

Expand All @@ -146,21 +149,21 @@ class ObjectProcessor {
}

let label:string=md.CustomObject.label||mem.member.name;
contentGroup.menuItems.push({href: mem.member.name,
contentGroup.menuItems.push({href: mem.member.name,
description: '',
title: label,
warning: false,
error: false
});

let contentObj:ObjectContent={name: mem.member.name,
let contentObj:ObjectContent={name: mem.member.name,
label: label,
sfObject: md.CustomObject,
fields: [],
validationRules: [],
recordTypes: []};
contentGroup.objects.push(contentObj);

this.processFields(mem.member, contentObj);
this.processValidationRules(mem.member, contentObj);
this.processRecordTypes(mem.member, contentObj);
Expand Down Expand Up @@ -217,11 +220,11 @@ class ObjectProcessor {
if ( (!field.label) && (-1==field.fullName.indexOf('__c')) ) {
field.label='N/A (standard field)';
}

if ( (!field.description) && (-1==field.fullName.indexOf('__c')) ) {
field.description='N/A (standard field)';
}

field.background=''; // change this if we need to callout anything
if (typeof field.description === 'undefined') {
field.background='orange';
Expand All @@ -241,7 +244,7 @@ class ObjectProcessor {
field.background='#f28a8a';
}
}

var type=fldMd.type;
if (type) {
type=type.toString();
Expand All @@ -255,13 +258,75 @@ class ObjectProcessor {
else {
type="N/A (standard field)";
}

field.additionalInfo=addAdditionalFieldInfo(fldMd, type);
field.fullType=type;

return field;
}

createERD(group : ObjectGroupContent) {
// TODO
// - conditionally based on config?
// - required field identifier

let entities:Array<PumlEntity> = [];
let relationships = {};
let groupMemberNames:Array<string> = [];
let relationTargetNames:Array<string> = [];

group.objects.forEach(object => {
groupMemberNames.push(object.name);
if (! object.name.includes("__mdt")) {
let entity:PumlEntity = {
name: object.name,
fields: []
};
relationships[object.name] = {};
object.fields.forEach(field => {
let pumlField:PumlField = {
name: field.fullName,
type: field.fullType
};
switch (field.fullType) {
case "Lookup":
case "MasterDetail" :
const relTarget = field.additionalInfo.split(': ')[1];
relationTargetNames.push(relTarget);
pumlField.typeAdditionalInfo = relTarget;
// We can use the below assignment to further enrich
relationships[object.name][relTarget] = true;
break;
case 'Text':
case "LongTextArea" :
pumlField.typeAdditionalInfo = field.additionalInfo.split(': ')[1];
break;
}
entity.fields.push(pumlField);
});
entities.push(entity);
}
});

// Create entities for non-group objects that are targets for relationships
const nonGroupMemberNames:Array<string> = relationTargetNames.filter(target => {
return !groupMemberNames.includes(target);
});

const pumlObj = {
entities: entities,
nonGroupEntities: nonGroupMemberNames,
relationships: relationships
}

this.generator.generateHTML(join('objects', 'objects-puml.ejs'), pumlObj)
.then(puml => {
writeFileSync(join(this.outputDir, group.name+'-erd.puml'), puml);
const gen = plantuml.generate(join(this.outputDir, group.name+'-erd.puml'), {format: 'svg'});
gen.out.pipe(createWriteStream(join(this.outputDir, group.name + '-erd.svg')));
});
}

}

export {ObjectProcessor}
8 changes: 8 additions & 0 deletions templates/common/styles.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@
nav .doc-header {
margin-bottom: -2em;
}

#erd a {
display: block;
}

#erd object {
pointer-events: none;
}
</style>
16 changes: 13 additions & 3 deletions templates/objects/group.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<li class="breadcrumb-item"><a href="objects.html">Objects</a></li>
<li class="breadcrumb-item active" aria-current="page"><%= content.title %></li>
</ol>
</nav>
</nav>
<div class="jumbotron jumbotron-fluid doc-header">
<div class="container">
<h1 class="display-4"><%= content.title %></h1>
Expand All @@ -29,12 +29,13 @@
<% content.menuItems.forEach(function(menuItem){ %>
<a class="nav-link pb-0" href="#<%= menuItem.href %>"><%= menuItem.title %></a>
<% }); %>
</nav>
<a class="nav-link pb-0" href="#erd">ERD</a>
</nav>
<div class="mt-5"></div>
<% content.objects.forEach(function(object){ %>
<div class="card mb-5">
<h3 class="card-header bg-info text-white"><a name="<%= object.name %>"><%= object.label %></a></h5>
<div class="card-body">
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted"><strong>API Name : </strong><%= object.name %></h6>
<p><%= object.sfObject.description %></p>
<% if (typeof object.recordTypes == 'object' && object.recordTypes && object.recordTypes.length>0) { %>
Expand Down Expand Up @@ -108,6 +109,15 @@
</div>
</div>
<% }); %>
<h3 class="card-header bg-info text-white"><a name="ERD">ERD</a></h3>
<div id="erd" class="text-center">
<a href="<%= content.name %>-erd.svg" target="_BLANK">
<object type="image/svg+xml" data="<%= content.name %>-erd.svg">
<!-- Fall back -->
<img src="<%= content.name %>-erd.svg" alt="ERD for <%= content.name %> object" />
</object>
</a>
</div>
</div>
<%- include ('../common/footer') %>
</body>
Expand Down
33 changes: 33 additions & 0 deletions templates/objects/objects-puml.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@startuml

'Hide the spot
hide circle

'Hide the stereotype
hide stereotype

hide <<external-group>> members

skinparam class {
BackgroundColor<<external-group>> LightBlue
}

'Entities
<% content.entities.forEach(function(object){ %> entity <%= object.name %> {
--
<% object.fields.forEach(function(field){ %> <%= field.name %> : <%= field.type %> <% if (field.typeAdditionalInfo) { %> (<%= field.typeAdditionalInfo %>) <% }; %>
<% }); %>
}
<% }); %>
'Non-group Entities
<% content.nonGroupEntities.forEach(function(nonGroupObject){ %> entity <%= nonGroupObject %> <<external-group>> {
}
<% }); %>

'Relationships
<% for (let src in content.relationships) {
for (let target in content.relationships[src]) { %>
<%= src %> }o..o| <%= target %>
<% }
}; %>
@enduml