ExportsHandler.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. no-unused-vars,
  6. standard/no-callback-literal,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS103: Rewrite code to no longer use __guard__
  14. * DS207: Consider shorter variations of null checks
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. let ExportsHandler, self
  18. const ProjectGetter = require('../Project/ProjectGetter')
  19. const ProjectHistoryHandler = require('../Project/ProjectHistoryHandler')
  20. const ProjectLocator = require('../Project/ProjectLocator')
  21. const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
  22. const UserGetter = require('../User/UserGetter')
  23. const logger = require('logger-sharelatex')
  24. let settings = require('settings-sharelatex')
  25. const async = require('async')
  26. let request = require('request')
  27. request = request.defaults()
  28. settings = require('settings-sharelatex')
  29. module.exports = ExportsHandler = self = {
  30. exportProject(export_params, callback) {
  31. if (callback == null) {
  32. callback = function(error, export_data) {}
  33. }
  34. return self._buildExport(export_params, function(err, export_data) {
  35. if (err != null) {
  36. return callback(err)
  37. }
  38. return self._requestExport(export_data, function(err, export_v1_id) {
  39. if (err != null) {
  40. return callback(err)
  41. }
  42. export_data.v1_id = export_v1_id
  43. // TODO: possibly store the export data in Mongo
  44. return callback(null, export_data)
  45. })
  46. })
  47. },
  48. _buildExport(export_params, callback) {
  49. if (callback == null) {
  50. callback = function(err, export_data) {}
  51. }
  52. const {
  53. project_id,
  54. user_id,
  55. brand_variation_id,
  56. title,
  57. description,
  58. author,
  59. license,
  60. show_source
  61. } = export_params
  62. const jobs = {
  63. project(cb) {
  64. return ProjectGetter.getProject(project_id, cb)
  65. },
  66. // TODO: when we update async, signature will change from (cb, results) to (results, cb)
  67. rootDoc: [
  68. 'project',
  69. (cb, results) =>
  70. ProjectRootDocManager.ensureRootDocumentIsValid(project_id, function(
  71. error
  72. ) {
  73. if (error != null) {
  74. return callback(error)
  75. }
  76. return ProjectLocator.findRootDoc(
  77. { project: results.project, project_id },
  78. cb
  79. )
  80. })
  81. ],
  82. user(cb) {
  83. return UserGetter.getUser(
  84. user_id,
  85. { first_name: 1, last_name: 1, email: 1, overleaf: 1 },
  86. cb
  87. )
  88. },
  89. historyVersion(cb) {
  90. return ProjectHistoryHandler.ensureHistoryExistsForProject(
  91. project_id,
  92. function(error) {
  93. if (error != null) {
  94. return callback(error)
  95. }
  96. return self._requestVersion(project_id, cb)
  97. }
  98. )
  99. }
  100. }
  101. return async.auto(jobs, function(err, results) {
  102. if (err != null) {
  103. logger.warn(
  104. { err, project_id, user_id, brand_variation_id },
  105. 'error building project export'
  106. )
  107. return callback(err)
  108. }
  109. const { project, rootDoc, user, historyVersion } = results
  110. if (!rootDoc || rootDoc[1] == null) {
  111. err = new Error('cannot export project without root doc')
  112. logger.warn({ err, project_id })
  113. return callback(err)
  114. }
  115. if (export_params.first_name && export_params.last_name) {
  116. user.first_name = export_params.first_name
  117. user.last_name = export_params.last_name
  118. }
  119. const export_data = {
  120. project: {
  121. id: project_id,
  122. rootDocPath: rootDoc[1] != null ? rootDoc[1].fileSystem : undefined,
  123. historyId: __guard__(
  124. project.overleaf != null ? project.overleaf.history : undefined,
  125. x => x.id
  126. ),
  127. historyVersion,
  128. v1ProjectId:
  129. project.overleaf != null ? project.overleaf.id : undefined,
  130. metadata: {
  131. compiler: project.compiler,
  132. imageName: project.imageName,
  133. title,
  134. description,
  135. author,
  136. license,
  137. showSource: show_source
  138. }
  139. },
  140. user: {
  141. id: user_id,
  142. firstName: user.first_name,
  143. lastName: user.last_name,
  144. email: user.email,
  145. orcidId: null, // until v2 gets ORCID
  146. v1UserId: user.overleaf != null ? user.overleaf.id : undefined
  147. },
  148. destination: {
  149. brandVariationId: brand_variation_id
  150. },
  151. options: {
  152. callbackUrl: null
  153. } // for now, until we want v1 to call us back
  154. }
  155. return callback(null, export_data)
  156. })
  157. },
  158. _requestExport(export_data, callback) {
  159. if (callback == null) {
  160. callback = function(err, export_v1_id) {}
  161. }
  162. return request.post(
  163. {
  164. url: `${settings.apis.v1.url}/api/v1/sharelatex/exports`,
  165. auth: { user: settings.apis.v1.user, pass: settings.apis.v1.pass },
  166. json: export_data
  167. },
  168. function(err, res, body) {
  169. if (err != null) {
  170. logger.warn(
  171. { err, export: export_data },
  172. 'error making request to v1 export'
  173. )
  174. return callback(err)
  175. } else if (res.statusCode >= 200 && res.statusCode < 300) {
  176. return callback(null, body.exportId)
  177. } else {
  178. logger.warn(
  179. { export: export_data },
  180. `v1 export returned failure; forwarding: ${body}`
  181. )
  182. // pass the v1 error along for the publish modal to handle
  183. return callback({ forwardResponse: body })
  184. }
  185. }
  186. )
  187. },
  188. _requestVersion(project_id, callback) {
  189. if (callback == null) {
  190. callback = function(err, export_v1_id) {}
  191. }
  192. return request.get(
  193. {
  194. url: `${
  195. settings.apis.project_history.url
  196. }/project/${project_id}/version`,
  197. json: true
  198. },
  199. function(err, res, body) {
  200. if (err != null) {
  201. logger.warn(
  202. { err, project_id },
  203. 'error making request to project history'
  204. )
  205. return callback(err)
  206. } else if (res.statusCode >= 200 && res.statusCode < 300) {
  207. return callback(null, body.version)
  208. } else {
  209. err = new Error(
  210. `project history version returned a failure status code: ${
  211. res.statusCode
  212. }`
  213. )
  214. logger.warn(
  215. { err, project_id },
  216. `project history version returned failure status code: ${
  217. res.statusCode
  218. }`
  219. )
  220. return callback(err)
  221. }
  222. }
  223. )
  224. },
  225. fetchExport(export_id, callback) {
  226. if (callback == null) {
  227. callback = function(err, export_json) {}
  228. }
  229. return request.get(
  230. {
  231. url: `${settings.apis.v1.url}/api/v1/sharelatex/exports/${export_id}`,
  232. auth: { user: settings.apis.v1.user, pass: settings.apis.v1.pass }
  233. },
  234. function(err, res, body) {
  235. if (err != null) {
  236. logger.warn(
  237. { err, export: export_id },
  238. 'error making request to v1 export'
  239. )
  240. return callback(err)
  241. } else if (res.statusCode >= 200 && res.statusCode < 300) {
  242. return callback(null, body)
  243. } else {
  244. err = new Error(
  245. `v1 export returned a failure status code: ${res.statusCode}`
  246. )
  247. logger.warn(
  248. { err, export: export_id },
  249. `v1 export returned failure status code: ${res.statusCode}`
  250. )
  251. return callback(err)
  252. }
  253. }
  254. )
  255. },
  256. fetchDownload(export_id, type, callback) {
  257. if (callback == null) {
  258. callback = function(err, file_url) {}
  259. }
  260. return request.get(
  261. {
  262. url: `${
  263. settings.apis.v1.url
  264. }/api/v1/sharelatex/exports/${export_id}/${type}_url`,
  265. auth: { user: settings.apis.v1.user, pass: settings.apis.v1.pass }
  266. },
  267. function(err, res, body) {
  268. if (err != null) {
  269. logger.warn(
  270. { err, export: export_id },
  271. 'error making request to v1 export'
  272. )
  273. return callback(err)
  274. } else if (res.statusCode >= 200 && res.statusCode < 300) {
  275. return callback(null, body)
  276. } else {
  277. err = new Error(
  278. `v1 export returned a failure status code: ${res.statusCode}`
  279. )
  280. logger.warn(
  281. { err, export: export_id },
  282. `v1 export zip fetch returned failure status code: ${
  283. res.statusCode
  284. }`
  285. )
  286. return callback(err)
  287. }
  288. }
  289. )
  290. }
  291. }
  292. function __guard__(value, transform) {
  293. return typeof value !== 'undefined' && value !== null
  294. ? transform(value)
  295. : undefined
  296. }