damus-github-export

Damus issue data exported from github
git clone git://jb55.com/damus-github-export
Log | Files | Refs | README | LICENSE

export.js (6232B)


      1 const fs = require("fs");
      2 
      3 // Gets a single comment
      4 const getComment = async (octokit, values, issueNumber) => {
      5   return new Promise((resolve, reject) => {
      6     const issueOptions = octokit.issues.listComments.endpoint.merge({
      7       owner: values.userOrOrganization,
      8       repo: values.repo,
      9       issue_number: issueNumber,
     10     });
     11     octokit.paginate(issueOptions).then(
     12       (commentsData) => {
     13         resolve(commentsData);
     14       },
     15       (err) => {
     16         console.error(err);
     17         reject(err);
     18       }
     19     );
     20   });
     21 };
     22 
     23 // Given the full list of issues, returns back an array of all comments,
     24 // each with the issue data also included.
     25 const getFullCommentData = async (octokit, values, data, verbose = false) => {
     26   const fullComments = [];
     27   for (let i = 0; i < data.length; i++) {
     28     const issueObject = data[i];
     29     fullComments.push({
     30       issue: issueObject,
     31     });
     32 
     33     if (verbose === true) {
     34       console.log("getting comments for issue #: ", issueObject.number);
     35     }
     36     const commentsData = await getComment(octokit, values, issueObject.number);
     37     commentsData.forEach((comment) => {
     38       fullComments.push({
     39         issue: issueObject,
     40         comment: {
     41           user: comment.user.login,
     42           created_at: comment.created_at,
     43           updated_at: comment.updated_at,
     44           body: comment.body,
     45         },
     46       });
     47     });
     48   }
     49   return fullComments;
     50 }
     51 
     52 const writeFile = async (data, fileName = "export.json") => {
     53   return new Promise((resolve, reject) => {
     54 	fs.writeFile(fileName, JSON.stringify(data), "utf8", function (err) {
     55             if (err) {
     56               reject(new Error("Error writing the file."));
     57             } else {
     58               resolve(fileName);
     59             }
     60           });
     61   })
     62 }
     63 
     64 const twoPadNumber = (number) => {
     65   return String(number).padStart(2, "0");
     66 }
     67 
     68 const defaultExportColumns = (data) => {
     69   return data.map((issueObject) => {
     70     const ret = {
     71       number: issueObject.number,
     72       title: issueObject.title,
     73       state: issueObject.state,
     74       labels: "", // will be filled in below, if it exists
     75       milestone: "", // will be filled in below, if it exists
     76       user: "", // will be filled in below, if it exists
     77       assignee: "", // will be filled in below, if it exists
     78       assignees: "", // will be filled in below, if it exists
     79       created_at: issueObject.created_at,
     80       updated_at: issueObject.updated_at,
     81       closed_at: issueObject.closed_at !== null ? issueObject.closed_at : "",
     82       body: issueObject.body,
     83     };
     84     if (issueObject.user) {
     85       ret.user = issueObject.user.login;
     86     }
     87     if (issueObject.labels) {
     88       ret.labels = issueObject.labels
     89         .map((labelObject) => {
     90           return labelObject.name;
     91         })
     92         .join(",");
     93     }
     94     if (issueObject.assignee && issueObject.assignee.login) {
     95       ret.assignee = issueObject.assignee.login;
     96     }
     97     if (issueObject.assignees && issueObject.assignees.length > 0) {
     98       ret.assignees = issueObject.assignees
     99         .map((assigneeObject) => {
    100           return assigneeObject.login;
    101         })
    102         .join(",");
    103     }
    104     if (issueObject.milestone && issueObject.milestone.title) {
    105       ret.milestone = issueObject.milestone.title;
    106     }
    107     return ret;
    108   });
    109 };
    110 
    111 const getDataAttribute = (issueObject, attribute) => {
    112   if (attribute.indexOf(".") > 0) {
    113     const parts = attribute.split(".");
    114     let currentObject = issueObject;
    115     parts.forEach((part) => {
    116       if (
    117         currentObject &&
    118         currentObject !== "" &&
    119         Object.prototype.hasOwnProperty.call(currentObject, part)
    120       ) {
    121         currentObject = currentObject[part];
    122       } else {
    123         currentObject = "";
    124       }
    125     });
    126     return currentObject;
    127   } else {
    128     return issueObject[attribute];
    129   }
    130 };
    131 
    132 const specificAttributeColumns = (data, attributes) => {
    133   return data.map((issueObject) => {
    134     const ret = {};
    135     attributes.forEach((attribute) => {
    136       ret[attribute] = getDataAttribute(issueObject, attribute);
    137     });
    138     return ret;
    139   });
    140 };
    141 
    142 const exportIssues = (octokit, values) => {
    143   // Getting all the issues:
    144   const options = octokit.issues.listForRepo.endpoint.merge({
    145     owner: values.userOrOrganization,
    146     repo: values.repo,
    147     state: "all",
    148   });
    149   octokit.paginate(options).then(
    150     async (data) => {
    151       // default export - columns that are compatible to be imported into GitHub
    152       let filteredData = defaultExportColumns(data);
    153       if (values.exportAll) {
    154         // Just pass "data", it will flatten the JSON object we got from the API and use that (lots of data!)
    155         filteredData = data;
    156       } else if (values.exportAttributes) {
    157         filteredData = specificAttributeColumns(data, values.exportAttributes);
    158       }
    159 
    160       // Add on comments, if requested.
    161       let csvData = filteredData;
    162       if (values.exportComments === true) {
    163         if (values.exportComments === true) {
    164           // If we want comments, replace the data that will get pushed into
    165           // the CSV with our full comments data:
    166           if (
    167             csvData[0] &&
    168             Object.prototype.hasOwnProperty.call(csvData[0], "number")
    169           ) {
    170             csvData = await getFullCommentData(
    171               octokit,
    172               values,
    173               csvData,
    174               values.verbose
    175             );
    176           } else {
    177             console.error(
    178               "Error: Must include issue number when exporting comments."
    179             );
    180             csvData = false;
    181           }
    182         }
    183       }
    184 
    185       // write the data out to file.
    186       writeFile(csvData, values.exportFileName || `${values.repo}.json`).then(
    187         (fileName) => {
    188           console.log(`Success! check ${fileName}`);
    189           console.log(
    190             "❤ ❗ If this project has provided you value, please ⭐ star the repo to show your support: ➡ https://github.com/gavinr/github-csv-tools"
    191           );
    192           process.exit(0);
    193         },
    194         (err) => {
    195           console.log("Error writing the file. Please try again.");
    196           console.error(err);
    197           process.exit(0);
    198         }
    199       );
    200     },
    201     (err) => {
    202       console.log("error", err);
    203       process.exit(0);
    204     }
    205   );
    206 };
    207 
    208 module.exports = { exportIssues };