import axios from "axios";
import { PollTimeout } from "./error.js";
import { Projects } from "./resources/Projects.js";
import { Assets } from "./resources/Assets.js";
import { Jobs } from "./resources/Jobs.js";
import { Workflows } from "./resources/Workflows.js";
const DEFAULT_BASE_URL = "https://api.metafold3d.com";
/** Metafold REST API client. */
class MetafoldClient {
projectID;
/**
* Endpoint for querying user information.
* @type {Projects}
*/
projects;
/**
* Endpoint for managing asset resources.
* @type {Assets}
*/
assets;
/**
* Endpoint for managing job resources.
* @type {Jobs}
*/
jobs;
/**
* Endpoint for managing workflow resources.
* @type {Workflows}
*/
workflows;
/** Underlying HTTP client. */
axios;
get;
put;
post;
patch;
delete;
/**
* Create a client.
*
* @param {string} accessToken - Metafold API secret key.
* @param {string} [projectID] - ID of the project to make API calls against.
* @param {string} [baseURL] - Metafold API URL. Used for internal testing.
*/
constructor(accessToken, projectID, baseURL = DEFAULT_BASE_URL) {
this.projectID = projectID;
this.axios = axios.create({
baseURL,
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${accessToken}`,
},
});
this.axios.interceptors.response.use((r) => r, (err) => {
if (err.response) {
const r = err.response;
// Error responses aren't entirely consistent in the Metafold API,
// for now we check for a handful of possible fields.
if (r.data.errors) {
const msg = r.data.errors.map((e) => {
if (e.field) {
return ` [${e.field}] ${e.msg}`;
}
return ` ${e.msg}`;
}).join("\n");
return Promise.reject(new Error(`Bad request:\n${msg}`, { cause: err }));
}
const reason = r.data?.msg ?? r.data?.description;
return Promise.reject(new Error(`${reason ?? r.statusText}`, { cause: err }));
}
return Promise.reject(err);
});
this.get = this.axios.get;
this.put = this.axios.put;
this.post = this.axios.post;
this.patch = this.axios.patch;
this.delete = this.axios.delete;
this.projects = new Projects(this);
this.assets = new Assets(this);
this.jobs = new Jobs(this);
this.workflows = new Workflows(this);
}
/**
* Poll the given URL every one second.
*
* Helpful for waiting on job results given a status URL.
*
* @param {string} url - Workflow status url.
* @param {number} [timeout=12000] - Time in seconds to wait for a result.
* @param {number} [every=1] - Frequency in seconds.
* @returns HTTP response.
*/
poll(url, timeout = 1000 * 60 * 2, every = 1) {
return new Promise((resolve, reject) => {
/* eslint-disable prefer-const */
let intervalID;
let timeoutID;
/* eslint-enable prefer-const */
const clearTimers = () => {
clearInterval(intervalID);
clearTimeout(timeoutID);
};
intervalID = setInterval(() => {
this.get(url)
.then((r) => {
if (r.status === 202) {
return;
}
clearTimers();
resolve(r);
})
.catch((e) => {
clearTimers();
reject(e);
});
}, 1000 * every);
timeoutID = setTimeout(() => {
clearInterval(intervalID);
reject(new PollTimeout("Polling timed out"));
}, timeout);
});
}
}
export default MetafoldClient;
export * from "./func.js";
export * from "./func-types.js";