resources/Jobs.js

  1. import { PollTimeout } from "../error.js";
  2. import { constructParams } from "../util.js";
  3. function job(j) {
  4. return {
  5. ...j,
  6. created: new Date(j.created),
  7. assets: j.assets?.map((a) => ({
  8. ...a,
  9. created: new Date(a.created),
  10. modified: new Date(a.modified),
  11. }))
  12. };
  13. }
  14. /** Metafold jobs endpoint. */
  15. export class Jobs {
  16. client;
  17. constructor(client) {
  18. this.client = client;
  19. }
  20. /**
  21. * List jobs.
  22. *
  23. * @param {Object} [params] - Optional list parameters.
  24. * @param {string} [params.sort] - Sort string. For details on syntax see the Metafold API docs.
  25. * Supported sorting fields are: "id", "name", or "created".
  26. * @param {string} [params.q] - Query string. For details on syntax see the Metafold API docs.
  27. * Supported search fields are: "id", "name", "type", and "state".
  28. * @returns List of job resources.
  29. */
  30. async list({ sort, q } = {}) {
  31. const params = constructParams({ sort, q });
  32. const r = await this.client.get(`/projects/${this.client.projectID}/jobs`, { params });
  33. return r.data.map(job);
  34. }
  35. /**
  36. * Get a job.
  37. *
  38. * @param {string} id - ID of job to get.
  39. * @returns Job resource.
  40. */
  41. async get(id) {
  42. const r = await this.client.get(`/projects/${this.client.projectID}/jobs/${id}`);
  43. return job(r.data);
  44. }
  45. /**
  46. * Dispatch a new job and wait for the result.
  47. *
  48. * See Metafold API docs for the full list of jobs.
  49. *
  50. * @param {string} type - Job type.
  51. * @param {Object} params - Job parameters.
  52. * @param {string} [name] - Job name.
  53. * @param {number} [timeout=12000] - Time in seconds to wait for a result.
  54. * @returns Completed job resource.
  55. */
  56. async run(type, params, name, timeout = 1000 * 60 * 2) {
  57. const url = await this.runStatus(type, params, name);
  58. let r = null;
  59. try {
  60. r = await this.poll(url, timeout);
  61. }
  62. catch (e) {
  63. if (e instanceof PollTimeout) {
  64. throw new Error(`Job '${name ?? type}' failed to complete within ${timeout} ms`, { cause: e });
  65. }
  66. else {
  67. throw e;
  68. }
  69. }
  70. return job(r.data);
  71. }
  72. /**
  73. * Dispatch a new job and return immediately without waiting for result.
  74. *
  75. * See Metafold API docs for the full list of jobs.
  76. *
  77. * @param {string} type - Job type.
  78. * @param {Object} params - Job parameters.
  79. * @param {string} [name] - Job name.
  80. * @returns {string} Job status url.
  81. */
  82. async runStatus(type, params, name) {
  83. const data = constructParams({ type, parameters: params, name });
  84. const r = await this.client.post(`/projects/${this.client.projectID}/jobs`, data, {
  85. headers: { "Content-Type": "application/json" },
  86. });
  87. return r.data.link;
  88. }
  89. /**
  90. * Poll the given URL every one second.
  91. *
  92. * Helpful for waiting on job results given a status URL.
  93. *
  94. * @param {string} url - Job status url.
  95. * @param {number} [timeout=12000] - Time in seconds to wait for a result.
  96. * @param {number} [every=1] - Frequency in seconds.
  97. * @returns HTTP response.
  98. */
  99. poll(url, timeout = 1000 * 60 * 2, every = 1) {
  100. return new Promise((resolve, reject) => {
  101. /* eslint-disable prefer-const */
  102. let intervalID;
  103. let timeoutID;
  104. /* eslint-enable prefer-const */
  105. const clearTimers = () => {
  106. clearInterval(intervalID);
  107. clearTimeout(timeoutID);
  108. };
  109. intervalID = setInterval(() => {
  110. this.client.get(url)
  111. .then((r) => {
  112. if (r.status === 202) {
  113. return;
  114. }
  115. clearTimers();
  116. resolve(r);
  117. })
  118. .catch((e) => {
  119. clearTimers();
  120. reject(e);
  121. });
  122. }, 1000 * every);
  123. timeoutID = setTimeout(() => {
  124. clearInterval(intervalID);
  125. reject(new PollTimeout("Job timed out"));
  126. }, timeout);
  127. });
  128. }
  129. /**
  130. * Update a job.
  131. *
  132. * @param {string} id - ID of job to update.
  133. * @param {Object} [params] - Optional update parameters.
  134. * @param {string} [params.name] - New job name. The existing name remains unchanged if undefined.
  135. * @returns Updated job resource.
  136. */
  137. async update(id, { name } = {}) {
  138. const data = constructParams({ name });
  139. const r = await this.client.patch(`/projects/${this.client.projectID}/jobs/${id}`, data);
  140. return job(r.data);
  141. }
  142. }