@maximai/maxim-js
Version:
Maxim AI JS SDK. Visit https://getmaxim.ai for more info.
383 lines • 15.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MaximDatasetAPI = void 0;
const dataset_1 = require("../models/dataset");
const maxim_1 = require("./maxim");
const platform_1 = require("../platform");
class MaximDatasetAPI extends maxim_1.MaximAPI {
constructor(baseUrl, apiKey, isDebug) {
super(baseUrl, apiKey, isDebug);
}
async addDatasetEntries(datasetId, datasetEntries) {
let nextRowNo = await this.getDatasetTotalRows(datasetId) + 1;
const entriesWithFileAttachments = [];
const transformedEntries = datasetEntries.map((entry) => {
const rowNo = nextRowNo++;
const isFile = entry.cellValue.type === dataset_1.VariableType.FILE;
if (isFile) {
entriesWithFileAttachments.push({ ...entry, rowNo });
}
return {
rowNo,
columnName: entry.columnName,
type: entry.cellValue.type,
value: isFile ? [] : entry.cellValue.payload,
};
});
const response = await this.fetch(`/api/sdk/v4/datasets/entries`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
datasetId,
entries: transformedEntries
}),
});
// Handle the response
if ("error" in response) {
throw new Error(response.error.message);
}
const mapEntryIDToEntries = {};
for (const entry of entriesWithFileAttachments) {
for (const cell of response.data.cells) {
if (cell.columnName === entry.columnName && cell.rowNo === entry.rowNo) {
entry.columnId = cell.columnId;
mapEntryIDToEntries[cell.entryId] = entry;
break;
}
}
}
const filePayloads = await Promise.all(Object.entries(mapEntryIDToEntries).map(([entryId, entry]) => this.uploadFileAttachments(datasetId, entryId, entry)));
// Transform file payloads to the updates shape required by the API schema
const transformedUpdates = filePayloads.map((payload) => ({
entryId: payload.entryId,
columnName: mapEntryIDToEntries[payload.entryId].columnName,
value: {
type: "file",
payload: payload,
},
}));
if (transformedUpdates.length > 0) {
try {
await this.updateDatasetEntries(datasetId, transformedUpdates);
}
catch (error) {
throw new Error(`Failed to update dataset entries: ${error}`);
}
}
}
async uploadFileAttachments(datasetId, entryId, entry) {
var _a;
const fileAttachments = [];
for (const file of entry.cellValue.payload) {
let file_attachment;
let { fileData, mimeType, size } = await this.processAttachment(file);
if (!mimeType || mimeType === 'application/octet-stream') {
const source = (_a = file.name) !== null && _a !== void 0 ? _a : (file.type === "file" ? file.path : undefined);
if (source) {
const inferredType = platform_1.platform.mime.lookup(source);
if (inferredType) {
mimeType = inferredType;
}
}
}
if (file.type !== "url") {
const signedURLResponse = await this.getUploadUrlForDatasetAttachment(datasetId, entryId, entry.columnId, file.id, mimeType, size);
try {
await this.uploadToSignedUrl(signedURLResponse.url, fileData, mimeType, {
filename: file.name,
entryId: entryId
});
}
catch (error) {
throw new Error(`Failed to upload file ${file.name} to signed URL: ${error}`);
}
file_attachment = {
id: file.id,
url: signedURLResponse.url,
hosted: true,
prefix: signedURLResponse.key,
props: {
"size": size,
"type": mimeType
}
};
}
else {
file_attachment = {
id: file.id,
url: file.url,
hosted: false,
prefix: "",
props: {
"size": size,
"type": mimeType
}
};
}
fileAttachments.push(file_attachment);
}
const filePayload = {
files: fileAttachments,
entryId: entryId
};
return filePayload;
}
async processAttachment(attachment) {
if (attachment.type === "url") {
return this.processUrlAttachment(attachment);
}
else if (attachment.type === "fileData" || attachment.type === "file") {
return this.processFileAttachment(attachment);
}
else {
throw new Error(`Invalid attachment type: ${attachment.type}. Expected url, fileData, or file.`);
}
}
async processUrlAttachment(attachment) {
try {
// Validate URL
if (!attachment.url || (!attachment.url.startsWith('http://') && !attachment.url.startsWith('https://'))) {
throw new Error(`Invalid URL: ${attachment.url}`);
}
// Get file info from HEAD request
const headResponse = await fetch(attachment.url, { method: 'HEAD' });
if (!headResponse.ok) {
throw new Error(`HTTP ${headResponse.status}: ${headResponse.statusText}`);
}
const mimeType = headResponse.headers.get('content-type') || 'application/octet-stream';
const contentLength = headResponse.headers.get('content-length');
// Calculate size
const size = contentLength ? parseInt(contentLength, 10) : 0;
return { fileData: null, mimeType, size };
}
catch (error) {
if (error instanceof Error && error.message.includes('Invalid URL')) {
throw error;
}
throw new Error(`Failed to download URL attachment ${attachment.url}: ${error instanceof Error ? error.message : String(error)}`);
}
}
async processFileAttachment(attachment) {
try {
let fileData;
let mimeType;
let size;
const maxFileSizeBytes = 1024 * 1024 * 100; // 100MB
if (attachment.type === "fileData") {
fileData = attachment.data;
mimeType = attachment.mimeType || 'application/octet-stream';
size = fileData.length;
if (size > maxFileSizeBytes) {
throw new Error(`File size exceeds the maximum allowed size of ${maxFileSizeBytes} bytes`);
}
}
else {
if (!platform_1.platform.features.fileIoSupported) {
throw new Error("File operations are not supported in this environment");
}
let stats;
try {
stats = await platform_1.platform.fs.readFile(attachment.path);
}
catch (error) {
throw new Error(`File not found: ${attachment.path}`);
}
if (stats.data.length > maxFileSizeBytes) {
throw new Error(`File size exceeds the maximum allowed size of ${maxFileSizeBytes} bytes`);
}
try {
fileData = Buffer.from(stats.data);
}
catch (error) {
throw new Error(`File not found: ${attachment.path}`);
}
mimeType = attachment.mimeType || 'application/octet-stream';
size = fileData.length;
}
return { fileData, mimeType, size };
}
catch (error) {
if (error instanceof Error && (error.message.includes('File size exceeds the maximum allowed size') ||
error.message.includes('File not found'))) {
throw error;
}
const attachmentName = attachment.name || 'unknown';
throw new Error(`Failed to process file attachment ${attachmentName}: ${error instanceof Error ? error.message : String(error)}`);
}
}
async getDatasetTotalRows(datasetId) {
return new Promise((resolve, reject) => {
this.fetch(`/api/sdk/v1/datasets/total-rows?datasetId=${datasetId}`)
.then((response) => {
if ("error" in response) {
reject(response.error);
}
else {
resolve(response.data);
}
})
.catch((error) => {
reject(error);
});
});
}
async getDatasetRow(datasetId, rowIndex) {
return new Promise((resolve, reject) => {
this.fetch(`/api/sdk/v2/datasets/row?datasetId=${datasetId}&row=${rowIndex}`)
.then((response) => {
if ("error" in response) {
reject(response.error);
}
else {
resolve(response.data);
}
})
.catch((error) => {
reject(error);
});
});
}
async getDatasetDatastructure(datasetId) {
return new Promise((resolve, reject) => {
this.fetch(`/api/sdk/v1/datasets/structure?datasetId=${datasetId}`)
.then((response) => {
if ("error" in response) {
reject(response.error);
}
else {
resolve(response.data);
}
})
.catch((error) => {
reject(error);
});
});
}
async getUploadUrlForDatasetAttachment(datasetId, entryId, columnId, key, mimeType, size) {
var _a;
try {
const response = await this.fetch("/api/sdk/v4/datasets/entries/attachments/", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
datasetId: datasetId,
entryId: entryId,
columnId: columnId,
file: {
id: key,
type: mimeType,
size: size,
}
}),
});
if (this.isDebug) {
console.log("Upload URL Response: ", response);
}
// Handle the response
if ("error" in response) {
throw new Error(response.error.message);
}
return {
url: response.data.url,
key: response.data.key
};
}
catch (error) {
// Handle axios errors with response data
if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error;
if ((_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) {
const errorData = axiosError.response.data;
if (errorData &&
typeof errorData === 'object' &&
'error' in errorData &&
typeof errorData.error === 'object' &&
'message' in errorData.error) {
throw new Error(errorData.error.message);
}
}
}
// Re-throw the error if it's already an Error instance
if (error instanceof Error) {
throw error;
}
// For any other type of error, convert to Error
throw new Error(String(error));
}
}
async uploadToSignedUrl(url, data, mimeType, fileContext) {
try {
const response = await this.axiosInstance.put(url, data, {
headers: {
"Content-Type": mimeType,
"Content-Length": data.length.toString(),
},
responseType: "text",
timeout: 120000,
transformRequest: [(data) => data],
transformResponse: [(data) => data],
baseURL: "",
});
if (response.status >= 200 && response.status < 300) {
return;
}
if (response.data && typeof response.data === "object" && "error" in response.data) {
throw response.data.error;
}
throw response.data;
}
catch (error) {
// Wrap network/DNS errors with file context for better debugging
const context = fileContext ?
` (file: ${fileContext.filename || 'unknown'}, entryId: ${fileContext.entryId || 'unknown'})` : '';
if (error instanceof Error) {
throw new Error(`Failed to upload file to signed URL${context}: ${error.message}`);
}
throw new Error(`Failed to upload file to signed URL${context}: ${String(error)}`);
}
}
async updateDatasetEntries(datasetId, updates) {
var _a;
try {
const response = await this.fetch("/api/sdk/v4/datasets/entries", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
datasetId: datasetId,
updates: updates
}),
});
if (this.isDebug) {
console.log("Update dataset entries: ", response);
}
// Handle the response
if (response.error) {
throw new Error(response.error.message);
}
}
catch (error) {
// Handle axios errors with response data
if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error;
if ((_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) {
const errorData = axiosError.response.data;
if (errorData &&
typeof errorData === 'object' &&
'error' in errorData &&
typeof errorData.error === 'object' &&
'message' in errorData.error) {
throw new Error(errorData.error.message);
}
}
}
// Re-throw the error if it's already an Error instance
if (error instanceof Error) {
throw new Error(`Failed to update dataset entries with attachments: ${error.message}`);
}
// For any other type of error, convert to Error
throw new Error(`Failed to update dataset entries with attachments: ${String(error)}`);
}
}
}
exports.MaximDatasetAPI = MaximDatasetAPI;
//# sourceMappingURL=dataset.js.map