Documenting Module Fields and Relationships

I am trying to get a betting understanding of the Corteza module structures and created a simply python script to create a plantuml diagram for the fields and relationships for all the modules.

Enjoy!

Hi @HSorensen. This looks very cool!

@HSorensen Quite cool; is the code publicly available? People may be interested in visualising their custom configurations.

Do you have a repo for this ?
I don’t claim any price for beauty, but this works for me:

# type: ignore
# Read properties from spike.properties file
# - Connect to Corteza server
# - Use JWT as authentication
#
# Generate ER diagram (will take some time):
# python3 corteza_entites.py > corteza_entities.plantuml
# java -jar plantuml.jar -progress -nbthread auto -verbose -o ./diagram corteza_entities.plantuml 
#
import sys
import configparser

import json
import requests
from sortedcontainers import SortedList


def read_config(cfg_file):
    """a simple function to read an array of configuration files into a config object

    """
    if cfg_file is not None:
        config = configparser.ConfigParser()
        config.read_file(cfg_file)
        return config

def getNamespaceID(slug="crm"):
    data = getNamespace(slug)
    return data['response']['set'][0]['namespaceID']

def getNamespace(slug="crm"):
    return queryCompose("namespace/?slug="+slug)

def getModules(namespaceID):
    return queryCompose("namespace/"+namespaceID+"/module/")

def queryCompose(query):
    CZA_URL = "http://" + CORTEZA_IP
    CZA_URI = CZA_URL + "/api/compose/" + query

    #print(CZA_URI)

    CZA_HDR = {"Authorization": "Bearer " + CORTEZA_JWT}

    response = requests.get(CZA_URI, None, headers=CZA_HDR)
    if response.status_code == 200:
        try:
            dataJson = response.json()
            # print(json.dumps(dataJson, indent=2, sort_keys=True))
        except Exception as err:
            print(response.text)
            raise err
    else:
        print(response.status_code, response.text)
        raise RuntimeError("REQUESTS ERROR "+CZA_URI+ " "+ response.status_code +" "+ response.text)

    return dataJson

def buildUmlEntity(module,eid,refs):
    l1 = SortedList() # required
    l2 = SortedList() # optional fields

    mid = module['moduleID']
    if mid in refs:
        print('ERRROR: module ' + module['name'] + ', already processed')
        return ""
    refs[mid] = {}
    refs[mid]['eid']=eid
    refs[mid]['refs'] = []

    out='entity "' + module['name'] + '" as E' + str(eid) +' {\n'
    for f in module['fields']:
        if f['isRequired']:
            l1.add('  *' + f['name'] + ' : ' + f['kind'])
        else:
            l2.add('   ' + f['name'] + ' : ' + f['kind'])
        if f['kind'] == "Record":
            refs[mid]['refs'].append(f['options']['moduleID'])

    if len(l1)>0:
        out += '\n'.join(l1)
        out += '\n  --\n'
    out += '\n'.join(l2)
    
    out += '\n}\n'
    return out

def buildUmlEntityRelations(entityRefs):
    rel="}o--||"
    out = ""
    # print(len(entityRefs))
    for er in entityRefs.keys():
    # print (er, entityRefs[er]['eid'])
        if len(entityRefs[er]['refs'])>7:
            dummy=None
            # print("Too many refs: " + er + ":" + str(entityRefs[er]['refs']))
        else:
            for r in entityRefs[er]['refs']:
                out += "E" + str(entityRefs[er]['eid']) + ' ' + rel + ' ' + "E" + str(entityRefs[r]['eid']) + "\n"
    return out

def main(argv):
    if len(argv) > 1:
        variant = argv[1]
    else:
        variant = ""

    #
    # property file must have a section and properties:
    #    [spike-dev]
    #    CORTEZA_JWT=.....
    #    CORTEZA_IP=x.x.x.x
    global CORTEZA_JWT
    global CORTEZA_IP
    spike_config = read_config(open("spike.properties"))
    CORTEZA_JWT = spike_config["spike" + variant]["CORTEZA_JWT"]
    CORTEZA_IP = spike_config["spike" + variant]["CORTEZA_IP"]

    # Map module ID to entity 
    # { <moduleid> : { eid:"Enn", refs:[<moduleid>,...] }
    # }
    moduleidRefs = {}

    # Keep track of entity count to create relationship
    entitycnt = -1
    out_entity_uml = "@startuml\nleft to right direction\n"

    NAMESPACEID=getNamespaceID("crm")
    modules=getModules(NAMESPACEID)
    # print(json.dumps(modules, indent=2, sort_keys=True))

    for m in modules['response']['set']:
        entitycnt += 1
        out_entity_uml += buildUmlEntity(m,entitycnt,moduleidRefs)

    out_entity_uml += buildUmlEntityRelations(moduleidRefs)
    out_entity_uml += "\n@enduml\n"
    print(out_entity_uml)
    return

if __name__ == "__main__":
    main(sys.argv)
1 Like

I don’t think we have a repository for such things (\cc @darh).
You can open a public GitHub repository and we can reference this tool from the documentation.