Advertisement
Guest User

Untitled

a guest
Mar 18th, 2019
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const fs = require('fs');
  2.  
  3. const methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH', 'TRACE', 'CONNECT'];
  4.  
  5. const jtt = {
  6.   namespaceStore: {},
  7.   typeData: "",
  8.   v: false,
  9.   init: function(){
  10.     console.log("jtt: Grabbing config from " + __dirname + '/jtt-config.json');
  11.     this.config = JSON.parse(fs.readFileSync(__dirname + '/jtt-config.json', 'utf8'));
  12.  
  13.     if(process.argv[2] && process.argv[2] === "v" || process.argv[2] === "verbose" || process.argv[2] === "-v" || process.argv[2] === "--verbose"){
  14.       this.v = true;
  15.     }
  16.  
  17.     if(this.config.useTree){
  18.       this.getAPISchemaFromTree();
  19.     } else {
  20.       this.getSchemaFromFile();
  21.     }
  22.   },
  23.  
  24.   //starts logging and invokes necessary methods to start processing
  25.   getAPISchemaFromTree: function(){
  26.     console.log("jtt: ...");
  27.     console.log("jtt: Building type definitions from directory: " + this.config.input);
  28.  
  29.     console.log("jtt: ...");
  30.     this.generateFlatSchemas();
  31.  
  32.     this.processDirectory(this.config.input);
  33.  
  34.     if(this.v){
  35.       console.log("jtt: ...");
  36.       console.log("jtt: Resulting store:");
  37.       console.log(JSON.stringify(this.namespaceStore));
  38.     }
  39.  
  40.     console.log("jtt:");
  41.     console.log("jtt: ...");
  42.     console.log("jtt: Generating ts file");
  43.     this.generateNativeTypes();
  44.  
  45.     if(this.v) {
  46.       console.log("jtt: ...");
  47.       console.log("jtt: Resulting types:");
  48.       console.log(JSON.stringify(this.typeData));
  49.     }
  50.  
  51.     this.saveTypes(this.typeData);
  52.   },
  53.  
  54.   //extracts schemas for a schemas folder with no directories,
  55.   //should not have endpoints here
  56.   generateFlatSchemas: function(){
  57.     const directory = this.slashit(this.config.input) + 'schemas/';
  58.     console.log("jtt: Generating flat schemas from:", directory);
  59.  
  60.     fs.readdirSync(directory).forEach(item => {
  61.       const dirStats = fs.statSync(directory + item);
  62.  
  63.       if(dirStats.isDirectory()) return;
  64.  
  65.       this.processFile(directory + item, this.namespaceStore);
  66.     });
  67.   },
  68.  
  69.   //recursively process directory to build endpoints and schemas, define method types, etc
  70.   //builds up namespace store, args are used for recursion
  71.   processDirectory: function(dir, endpoint = "", namespace = "", method = ""){
  72.     const directory = this.slashit(dir);
  73.  
  74.     fs.readdirSync(directory).forEach(item => {
  75.       if(item != "schemas"){
  76.         const dirStats = fs.statSync(directory + item);
  77.  
  78.         if(dirStats.isDirectory()) {
  79.           let methodName;
  80.           // if this current is a request method we know all the previous
  81.           // strings are the actual endpoint, so we can then store and name it
  82.           if(this.isMethod(item)){
  83.             methodName = item;
  84.             namespace = this.buildAPIEndpointName(endpoint);
  85.  
  86.             if(!this.namespaceStore[namespace]) {
  87.               this.namespaceStore[namespace] = {
  88.                 url: endpoint
  89.               };
  90.             }
  91.  
  92.             this.namespaceStore[namespace][item] = {
  93.               request: undefined,
  94.               response: undefined
  95.             };
  96.  
  97.             console.log("jtt: ");
  98.             console.log("jtt: Starting new namespace: ", namespace);
  99.             console.log("jtt:            at endpoint: ", endpoint);
  100.             console.log("jtt: ");
  101.             console.log("jtt: Generating schemas for:");
  102.           } else {
  103.             endpoint += '/' + item || "";
  104.           }
  105.  
  106.           this.processDirectory(directory + item, endpoint, namespace, methodName)
  107.         } else {
  108.           this.processFile(directory + item, this.namespaceStore[namespace][method]);
  109.         }
  110.       }
  111.     });
  112.  
  113.     return namespace;
  114.   },
  115.  
  116.   //processes single file to extract schema definition
  117.   processFile: function(file, store){
  118.     const refMatch = new RegExp(/(?:\$ref)/g),
  119.           fileData = JSON.parse(fs.readFileSync(file, 'utf8')),
  120.           fileName = this.getFilename(file);
  121.  
  122.     console.log("jtt:                          " + file);
  123.  
  124.     let namespace = "";
  125.  
  126.     if(fileName === "request" || fileName === "response"){
  127.       namespace = fileName;
  128.     } else {
  129.       namespace = this.buildAPIDefinitionName(fileName);
  130.     }
  131.  
  132.     store[namespace] = {
  133.       schema: {},
  134.       required: fileData.required || [],
  135.       pattern: fileData.pattern || "",
  136.       defaults: fileData.defaults || {}
  137.     };
  138.  
  139.     switch(fileData.type){
  140.       case "string":
  141.         store[namespace].schema = "string";
  142.         break;
  143.       case "object":
  144.         for(let x in fileData.properties){
  145.           if(fileData.properties[x]['$ref']){
  146.             store[namespace].schema[x] = {
  147.               "ref": this.buildAPIDefinitionName(fileData.properties[x]['$ref'])
  148.             }
  149.           } else {
  150.             store[namespace].schema[x] = fileData.properties[x].type;
  151.           }
  152.         }
  153.     };
  154.   },
  155.  
  156.   //builds up fat string using ts syntax to define native types, namespaces, etc
  157.   generateNativeTypes: function(){
  158.     let string = "";
  159.  
  160.     for(let n in this.namespaceStore){
  161.       string += "export namespace " + n + " { \r\n";
  162.  
  163.       //build flat def
  164.       if(n.indexOf('_DEF') > -1) string += this.generateSchemaString(this.namespaceStore[n]);
  165.  
  166.       //build ep def
  167.       if(n.indexOf('_EP') > -1) string += this.generateEndpointString(this.namespaceStore[n]);
  168.  
  169.       string += "}\r\n"
  170.     }
  171.  
  172.     this.typeData = string;
  173.   },
  174.  
  175.   generateEndpointString: function(namespace){
  176.     let string = "";
  177.     for(let prop in namespace){
  178.  
  179.       //build url prop
  180.       if(prop === 'url') {
  181.         string += '\xa0\ export const url = "' + namespace.url + '";\r\n';
  182.         continue;
  183.       }
  184.  
  185.       //all subsquent props will be request methods
  186.       string += "\xa0\ export namespace " + prop + " { \r\n";
  187.         for(let type in namespace[prop]){
  188.           string += "\xa0\xa0\ export namespace " + type + " { \r\n";
  189.             string += this.generateSchemaString(namespace[prop][type], "\xa0\xa0\xa0\ ");
  190.           string += "\xa0\xa0\ }\r\n";
  191.         }
  192.       string += "\xa0\ }\r\n"
  193.     }
  194.     return string;
  195.   },
  196.  
  197.   //generates all the string data the builds up a schema definition
  198.   generateSchemaString: function(namespace, indent = ""){
  199.     let string = indent + "\xa0\ export type schema = ";
  200.  
  201.     //build schema obj
  202.     if(typeof namespace.schema === "object"){
  203.       string += indent + "{ \r\n";
  204.       for(let prop in namespace.schema){
  205.         const isRequired = namespace.required.indexOf(prop) > -1;
  206.  
  207.         //if the prop in the schema is an obj, it's likely a reference
  208.         if(typeof namespace.schema[prop] === "object"){
  209.           if(namespace.schema[prop].ref){
  210.             string += indent + '\xa0\xa0\ "' + prop +'"' + (!isRequired ? '?' : '') + ': ' + namespace.schema[prop].ref + '.schema, \r\n';
  211.           }
  212.         } else {
  213.           string += indent + '\xa0\xa0\ "' + prop +'"' + (!isRequired ? '?' : '') + ': "' + namespace.schema[prop] + '", \r\n';
  214.         }
  215.  
  216.         //TODO: add array option here
  217.       }
  218.  
  219.       string += indent + "\xa0\ }\r\n"
  220.  
  221.     //if schema is simply string, do so
  222.     } else if (typeof namespace.schema === "string") {
  223.       string += "String;\r\n";
  224.     }
  225.  
  226.     //build required arr
  227.     if(namespace.required && namespace.required.length > 0){
  228.       string += indent + "\xa0\ export const required = [\r\n";
  229.         namespace.required.map((v) => string += indent + '\xa0\xa0\ "' + v + '", \r\n');
  230.       string += indent + "\xa0\ ];\r\n"
  231.     }
  232.  
  233.     //build defs Object
  234.     if(namespace.defaults && Object.keys(namespace.defaults).length > 0){
  235.       string += indent + "\xa0\ export const defaults = {\r\n";
  236.         Object.keys(namespace.defaults).map((v)=>{
  237.           const isString = typeof namespace.defaults[v] === "string";
  238.           string += indent + '\xa0\xa0\ "' + v + '": ';
  239.             string +=  isString ? '"' : '';
  240.             string += namespace.defaults[v];
  241.             string += isString ? '"' : '';
  242.           string += ', \r\n';
  243.         });
  244.       string += indent + "\xa0\ }\r\n"
  245.     }
  246.  
  247.     //build patterns obj
  248.     if(namespace.pattern && namespace.pattern !== ""){
  249.       string += indent + '\xa0\ export const pattern = "' + namespace.pattern + '";\r\n';
  250.     }
  251.  
  252.     return string;
  253.   },
  254.  
  255.   //decides whether or not we're in the last it of a loop to place a comma or not
  256.   comma: function(){},
  257.  
  258.   //builds namespace name for a single api schema def,
  259.   //just builds a name, no actual string data
  260.   buildAPIDefinitionName: function(fileName){
  261.     return fileName.toUpperCase() + "_DEF";
  262.   },
  263.  
  264.   //builds the namespace name, just the name itself, not the actual
  265.   //string data used for the native types so no brackets, no export, etc
  266.   buildAPIEndpointName: function(str){
  267.     let bracketReg = new RegExp(/{(.*?)}/g),
  268.         segments = str.split('/'),
  269.         name = "";
  270.  
  271.     //accounting for str that started with a slash
  272.     if(segments[0] === "") segments.shift();
  273.  
  274.     for(let x = 0; x < segments.length; x++){
  275.       //if current segment is a variable, we currently put an
  276.       //underscore but there could be better ways
  277.       if(bracketReg.test(segments[x])) {
  278.         name += segments[x].replace("{", "").replace("}", "").replace("-", "") + "_";
  279.       } else {
  280.         name += segments[x].toUpperCase() + "_";
  281.       }
  282.     }
  283.  
  284.     return name += "EP";
  285.   },
  286.  
  287.   //adds slash to end of string
  288.   slashit: function(string){
  289.     return string[string - 1] !== '/' ? string += '/' : string;
  290.   },
  291.  
  292.   //checks if string is a type of req method as per methods arr
  293.   isMethod: function(string){
  294.     for(let x = 0; x < methods.length; x++){
  295.       if(string === methods[x]){
  296.         return true;
  297.       }
  298.     }
  299.  
  300.     return false;
  301.   },
  302.  
  303.   //extracts filename from string
  304.   getFilename: function(str){
  305.     const segs = /(.*?)\.json/g.exec(str)[1].split('/');
  306.     return segs[segs.length-1];
  307.   },
  308.  
  309.   saveTypes: function(data){
  310.     console.log("jtt: ...");
  311.     console.log("jtt: Saving types to " + this.config.output + this.config.filename + ".ts");
  312.  
  313.     fs.writeFileSync(this.config.output + this.config.filename + ".ts", data, function(){
  314.       console.log("jtt: File saved");
  315.     }, function(err) {
  316.       if(err) {
  317.         return console.error(err);
  318.       }
  319.  
  320.       console.log("The file was saved!");
  321.     });
  322.   }
  323. }
  324.  
  325. jtt.init();
  326.  
  327. exports.jtt = jtt;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement