Zendro-dev / graphql-server-model-codegen

Command line utility to auto-generate the structure files for a graphql server
MIT License
1 stars 2 forks source link

Code Generator

Command line utility to generate the structure files that Zendro graphql-server will use to perform CRUD operations for each model created.

Set up

Clone the repository and run:

$ npm install -g

If you only want to install it locally run npm install instead

Examples of use - Code Generator

In the same directory of this repository run:

# -f <input-json-files>   directory where json models are stored
# -o <output-directory>   directory where the generated code will be written
$ code-generator -f ./example_json_files -o /your_path_directory

For help using this command:

$ code-generator -h

# Code generator for the GraphQL server
#
# Options:
#
#   -f, --jsonFiles <filesFolder>      Folder containing one json file for each model
#   -o, --outputDirectory <directory>  Directory where generated code will be written
#   -h, --help                         output usage information

This command will create four sub-folders within the output-directory folder, containing the generated files for each model:

models/      -> sequelize model
schemas/     -> graphQL schema
resolvers/   -> basic CRUD resolvers
migrations/  -> create and delete table migration file

To use the code generator with the graphql-server, use its path in the output-directory.

Development

To run the unit-test suite

$ npm run test-unit

To run the integration-test suite

$ npm run test-integration [-- OPTIONS]

To view the different integration-test commands and some examples

$ npm run test-integration -- -h

JSON files Spec

Each json file describes one and only one model. (i.e if an association involves two models, this association needs to be specified in both json files, corresponding to each model).

For each model we need to specify the following fields in the json file:

Name Type Description
model String Name of the model (it is recommended to Capitalize the name).
storageType String Type of storage where the model is stored. Currently supported types are sql, Webservice, and zendro_server
url String This field is only mandatory for zendro_server stored models. Indicates the URL of the Zendro server storing the model.
attributes Object The key of each entry is the name of the attribute. There are two options for the value: a string indicating the type of the attribute, or an object with two properties: type (the type of the attribute) and description (attribute description). See types-spec table below for allowed types. Example of option one: { "attribute1" : "String", "attribute2: "Int" } Example of option two: { "attribute1" : {"type" :"String", "description": "Some description"}, "attribute2: "Int
associations Object The key of each entry is the name of the association and the value should be an object describing the associations. See Associations Spec section below for the specifications of the associations.

EXAMPLES OF VALID JSON FILES

//Dog.json
{
  "model" : "Dog",
  "storageType" : "Sql",
  "attributes" : {
    "name" : "String",
    "breed" : "String",
    "personId": "Int"
  },

  "associations" : {
    "person" : {
      "type" : "many_to_one",
      "implementation": "foreignkeys",
      "reverseAssociation": "dogs",
      "target" : "Person",
      "targetKey" : "personId",
      "keysIn": "Dog",
      "targetStorageType" : "sql"
    }
  }
}
//Publisher.json
{
  "model" : "Publisher",
  "storageType" : "webservice",
  "attributes" : {
    "name" : "String",
    "phone" : "String"
  },
  "associations":{
      "books" : {
          "type" : "one_to_many",
          "implementation": "foreignkeys",
          "reverseAssociation": "publisher",
          "target" : "Book",
          "targetKey" : "publisherId",
          "keysIn" : "Book",
          "targetStorageType" : "sql"
        }
  }
}

Types Spec

The following types are allowed for the attributes field

Type
String
Int
Float
Boolean
Date
Time
DateTime

For more info about Date, Time, and DateTime types, please see the graphql-iso-date/rfc3339.txt.

Example:

Associations Spec

We will consider four types of associations according to the relation between associated records of the two models:

  1. one_to_one
  2. many_to_one
  3. one_to_many
  4. many_to_many

For all types of association, the necessary arguments would be:

name Type Description
type String Type of association (one_to_one, one_to_many, etc.)
implementation String implementation type of the association. Can be one of foreignkeys, generic or sql_cross_table (only for many_to_many)`
reverseAssociation String The name of the reverse association from the other model. This field is only mandatory for building the single-page-app, not for generating the the graphql-server code via this repository.
target String Name of model to which the current model will be associated with.
targetKey String A unique identifier of the association for the case where there appear more than one association with the same model.
keysIn String Name of the model where the targetKey is stored.
targetStorageType String Type of storage where the target model is stored. So far can be one of sql or Webservice.
label String Name of the column in the target model to be used as a display name in the GUI.
sublabel String Optional name of the column in the target model to be used as a sub-label in the GUI.

Note: The keysIn argument points to the model that stores the information about the foreignKey(s). That can be either a single key, a foreignkey array or a cross-model.

When the association is of type many_to_many it's necessary to describe an extra argument sourceKey:

name Type Description
sourceKey String Key to identify the source id

Be aware that in case of a many_to_many via an sql_cross_table implementation the keysIn field points to the cross model.

NOTE:

Be aware that in the case of this type of association the user is required to describe the cross table used in the field keysIn as a model in its own. For example, if we have a model User and a model Role and they are associated in a many_to_many way, then we also need to describe the role_to_user model:

//User model
{
  "model" : "User",
  "storageType" : "SQL",
  "attributes" : {
    "email" : "String",
    "password" : "String"
  },
  "associations" :{
    "roles" : {
      "type" : "many_to_many",
      "implementation": "foreignkeys",
      "reverseAssociation": "dogs",
      "target" : "Role",
      "targetKey" : "role_Id",
      "sourceKey" : "user_Id",
      "keysIn" : "role_to_user",
      "targetStorageType" : "sql",
      "label": "name"
    }
  }

}
//Role model
{
  "model" : "Role",
  "storageType" : "SQL",
  "attributes" : {
    "name" : "String",
    "description" : "String"
  },
  "associations" : {
    "users" : {
      "type" : "many_to_many",
      "target" : "User",
      "implementation": "sql_cross_table",
      "reverseAssociation": "roles",
      "targetKey" : "user_Id",
      "sourceKey" : "role_Id",
      "keysIn" : "role_to_user",
      "targetStorageType" : "sql",
      "label": "email"
    }
  }
}
//role_to_user model
{
  "model" : "role_to_user",
  "storageType" : "SQL",
  "attributes" : {
    "user_Id" : "Int",
    "role_Id" : "Int"
  }
}

NOTE:

It's important to notice that when a model involves a foreign key for the association, this key should be explicitly written into the attributes field of the given local model.

Example:

{
  "model" : "book",
  "storageType" : "sql",
  "attributes" : {
    "title" : {"type":"String", "description": "The book's title"},
    "publisher_id": "Int"
  },
  "associations":{
      "publisher" : {
        "type" : "many_to_one", // association type
        "implementation": "foreignkeys", // standard implementation via foreign keys
        "reverseAssociation": "dogs", // name of the association in the publisher model
        "target" : "publisher", // Model's name is `publisher`
        "targetKey" : "publisher_id", // Local alias for this association
        "keysIn": "book", // FK to publisher will be stored in the Book model
        "targetStorageType" : "Webservice", //  It's a remote database
        "label" : "name" // Show in GUI the name of the publisher taken from external DB
        }
  }
}

NOTE:

THE SAME DATA MODELS DESCRIPTION(.json files) WILL BE USEFUL FOR GENERATING BOTH, THE BACKEND DESCRIBED HERE AND THE FRONTEND OR GUI.

Fields label and sublabel in the specification are only needed by the GUI generator, but backend generator will only read required information, therefore extra fields such as label and sublabel will be ignored by the backend generator. Example:

//book.json
{
 "model" : "Book",
 "storageType" : "SQL",
 "attributes" : {
        "id" : "Int",
        "title" : {"type":"String", "description": "The book's title"},
        "ISBN": "Int"
    },
 "associations" : {
        "authors" : {
            "type" : "many_to_many",
            "implementation": "sql_cross_table",
            "reverseAssociation": "books",
            "target" : "Person",
            "targetKey" : "person_id",
            "sourceKey" : "book_id",
            "keysIn" : "person_to_book",
            "targetStorageType" : "sql",
            "label": "name",
            "sublabel": "lastname"
        }
    }
}

Testing

For relevant files see package.json (section scripts), directories .test and docker. Test framework is mocha and chai.

Contributions

Zendro is the product of a joint effort between the Forschungszentrum Jülich, Germany and the Comisión Nacional para el Conocimiento y Uso de la Biodiversidad, México, to generate a tool that allows efficiently building data warehouses capable of dealing with diverse data generated by different research groups in the context of the FAIR principles and multidisciplinary projects. The name Zendro comes from the words Zenzontle and Drossel, which are Mexican and German words denoting a mockingbird, a bird capable of “talking” different languages, similar to how Zendro can connect your data warehouse from any programming language or data analysis pipeline.

Zendro contributors in alphabetical order

Francisca Acevedo1, Vicente Arriaga1, Katja Dohm3, Constantin Eiteneuer2, Sven Fahrner2, Frank Fischer4, Asis Hallab2, Alicia Mastretta-Yanes1, Roland Pieruschka2, Alejandro Ponce1, Yaxal Ponce2, Francisco Ramírez1, Irene Ramos1, Bernardo Terroba1, Tim Rehberg3, Verónica Suaste1, Björn Usadel2, David Velasco2, Thomas Voecking3, Dan Wang2

Author affiliations

  1. CONABIO - Comisión Nacional para el Conocimiento y Uso de la Biodiversidad, México
  2. Forschungszentrum Jülich - Germany
  3. auticon - www.auticon.com
  4. InterTech - www.intertech.de

Zendro author contributions

Asis Hallab and Alicia Mastretta-Yanes coordinated the project. Asis Hallab designed the software. Programming of code generators, the browser based single page application interface, and the GraphQL application programming interface was done by Katja Dohm, Constantin Eiteneuer, Francisco Ramírez, Tim Rehberg, Veronica Suaste, David Velasco, Thomas Voecking, and Dan Wang. Counselling and use case definitions were contributed by Francisca Acevedo, Vicente Arriaga, Frank Fischer, Roland Pieruschka, Alejandro Ponce, Irene Ramos, and Björn Usadel. User experience and application of Zendro on data management projects was carried out by Asis Hallab, Alicia Mastretta-Yanes, Yaxal Ponce, Irene Ramos, Verónica Suaste, and David Velasco. Logo design was made by Bernardo Terroba.