@orvium/orvium-tools
Version:
Set of tools to interact with Orvium API
292 lines (291 loc) • 15.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.importDeposit = importDeposit;
/**
* Importer module for managing deposits and manuscript uploads to the Orvium platform.
*
* This module handles the full process of importing deposit data (including author information,
* metadata, keywords, community, and manuscript files) to the Orvium platform:
*
* - Load metadata from a local JSON file.
* - Transform author data into the appropriate format for the Orvium platform.
* - Create a deposit entry by sending the metadata and author information to the platform.
* - Generate a signed URL for securely uploading the manuscript to a cloud storage service (e.g., AWS S3).
* - Upload the manuscript file using the pre-signed URL.
* - Confirm the successful upload by sending metadata to the platform.
*
* The module uses `dotenv` for environment variables, `axios` for HTTP requests, and `fs` and `path`
* for file operations. It is designed to be used in automation processes or integrated into other
* applications for deposit and manuscript management.
*
*/
const dotenv_1 = __importDefault(require("dotenv"));
const axios_1 = __importDefault(require("axios"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
/**
* Load and prepare environment variables
*/
dotenv_1.default.config();
//Obtain the api key from the env variable
const API_KEY = process.env.API_KEY;
const API_KEY_USER = process.env.API_KEY_USER;
// Define the base URL and the required endpoint from the environment variables
const API_URL = process.env.API_URL;
const DEPOSIT_IMPORT_ENDPOINT = `${API_URL}/deposits/importBasicDeposit`;
const UPLOAD_ENDPOINT = `${API_URL}/deposits/:id/files`;
// Check if the necessary environment variable are provided, otherwise exit
if (!API_KEY || !API_KEY_USER || !API_URL) {
console.error('Missing required environment variables: API_KEY, API_USER and API_URL are not set.');
process.exit(1); // Exit the process with a status code of 1 for an error
}
/**
* Loads a JSON file from the specified file path and returns its content as an object.
* This function synchronously reads the file located at the given path and parses the JSON content.
*
* @param {string} directoryPath - The relative or absolute path to the dir with the meta JSON file to be loaded.
* @returns {Object} Returns the parsed JSON object from the file.
* @throws {Error} Throws an error if the file cannot be read or if the JSON cannot be parsed.
*/
function loadJsonFile(directoryPath) {
// Append 'meta.json' to the provided directory path
const metaPath = path_1.default.join(directoryPath, 'meta.json');
try {
// Reading the meta data json file
const absolutePath = path_1.default.resolve(metaPath);
const metaData = fs_1.default.readFileSync(absolutePath, 'utf8');
return JSON.parse(metaData);
}
catch (error) {
console.error('Error reading JSON file:', error);
throw error;
}
}
/**
* Prepares the file payload including metadata such as name, type, size, and last modification time.
* This function is specifically structured to fetch file metadata synchronously.
*
* @param {string} manuscriptPath - The full path to the file.
* @returns {object} The file payload object necessary for uploads.
*/
function createManuscriptMetadata(manuscriptPath) {
const fileStats = fs_1.default.statSync(manuscriptPath); // Get file statistics synchronously
return {
file: {
name: path_1.default.basename(manuscriptPath), // Extract the filename from the path
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // MIME type (adjust as necessary)
size: fileStats.size, // File size in bytes
lastModified: fileStats.mtimeMs // Last modified time in milliseconds
}
};
}
/**
* Transforms an author object from the input format to the desired format to be imported.
*
* @param {InputAuthor} inputAuthor - The author object in the input format containing the required properties.
* @returns {Author} Returns a new author object with properties formatted as firstName, lastName, and orcid.
*
*/
function transformAuthor(inputAuthor) {
return {
firstName: inputAuthor.first_name,
lastName: inputAuthor.last_name,
orcid: inputAuthor.orcid,
};
}
/**
* Submits a deposit object to a server via a POST request and returns the unique deposit ID.
* It expects the server response to include a unique identifier for the newly created deposit, which it then returns.
*
* @param {Deposit} deposit - The deposit object to be sent to the server.
* @returns {Promise<string>} A promise that resolves with the deposit's unique identifier as a string.
* @throws {Error} Throws an error if the deposit ID is not included in the server's response or if there is an issue with the HTTP request.
*/
function importSingleDeposit(deposit) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const depositWithoutManuscript = ((_a) => {
var { manuscript } = _a, rest = __rest(_a, ["manuscript"]);
return rest;
})(deposit);
const response = yield axios_1.default.post(DEPOSIT_IMPORT_ENDPOINT, depositWithoutManuscript, {
headers: {
'x-api-key': API_KEY,
'x-api-key-user': API_KEY_USER
}
});
if (response.data && response.data._id) {
return response.data._id; // Return the deposit ID
}
else {
throw new Error('Deposit ID not found in the response');
}
}
catch (error) {
console.error('Error importing deposit:', ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error.message);
throw error; // Properly handle the error by throwing
}
});
}
/**
* Asynchronously generates a signed URL for uploading a manuscript to S3, using manuscript metadata and deposit ID.
* This function makes a POST request to a specified endpoint to obtain a signed URL, which can then be used to upload the manuscript.
*
* @param {string} depositId - The unique identifier for the deposit to which the manuscript will be uploaded.
* @param {ManuscriptMetadata} manuscriptMetadata - The metadata for the manuscript that needs to be uploaded, including file name, type, size, etc.
* @returns {Promise<string>} A promise that resolves with the signed URL needed for uploading the manuscript.
* @throws {Error} Throws an error if the signed URL is not obtained from the response or if there is a network or server error.
*/
function generateManuscriptUploadUrl(depositId, manuscriptMetadata) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const url = `${UPLOAD_ENDPOINT.replace(':id', depositId)}?isMainFile=true&replacePDF=false`;
try {
const response = yield axios_1.default.post(url, manuscriptMetadata, {
headers: {
'x-api-key': API_KEY,
'x-api-key-user': API_KEY_USER // Use API key for authentication
}
});
if (response) {
return response.data; // Return the signed URL to upload the manuscritp
}
else {
throw new Error('Upload Signed Url not obtained');
}
}
catch (error) {
console.error('Error getting the signed URL to upload the manuscript:', ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error.message);
throw error; // Properly handle the error by throwing
}
});
}
/**
* Uploads a manuscript to an S3 bucket using a pre-signed URL obtained from a service.
* This function handles the streaming upload of a manuscript file located in a specified directory
* to an Amazon S3 bucket using an HTTP PUT request. The necessary headers for the request, such as
* Content-Type and Content-Length, are set based on the provided manuscript metadata.
*
* @param {string} manuscriptPath - The manuscript path where the manuscript is stored locally.
* @param {UploadSignedUrlResponse} uploadSignedUrlResponse - The response object containing the signed URL and other upload parameters.
* @param {ManuscriptMetadata} manuscriptMetadata - Metadata about the manuscript, including the file size and other attributes.
* @returns {Promise<AxiosResponse>} A promise that resolves with the response from the S3 service upon successful upload.
* @throws {Error} An error is thrown if the upload fails, with a message detailing the cause of the failure.
*/
function uploadManuscriptToSignedUrl(manuscriptPath, uploadSignedUrlResponse, manuscriptMetadata) {
return __awaiter(this, void 0, void 0, function* () {
const manuscritpStream = fs_1.default.createReadStream(manuscriptPath);
try {
const response = yield axios_1.default.put(uploadSignedUrlResponse.signedUrl, manuscritpStream, {
headers: {
'Content-Type': 'application/octet-stream', // Set appropriate content type
'Content-Length': manuscriptMetadata.file.size, // AWS S3 needs Content-Length set for PUT operations
}
});
return response;
}
catch (error) {
console.error('Error uploading manuscript to signed URL:', error.message);
throw error; // Properly handle the error by throwing
}
});
}
/**
* Confirms the upload of a manuscript by sending metadata (excluding the signed URL) to a server endpoint.
* This function makes an HTTP PATCH request to the server to confirm that a manuscript has been successfully
* uploaded to the designated storage based on the provided deposit ID. The function uses part of the upload
* response (metadata without the signed URL) to make this confirmation.
*
* @param {string} depositId - The unique identifier for the deposit to which the manuscript has been uploaded.
* @param {UploadSignedUrlResponse} uploadSignedUrlResponse - The response object from the upload attempt,
* containing the signed URL and other manuscript metadata.
* @returns {Promise<AxiosResponse>} A promise that resolves with the server's response to the confirmation request.
* @throws {Error} Throws an error if the confirmation fails, with a message detailing the cause of the failure.
*/
function confirmManuscriptImported(depositId, uploadSignedUrlResponse) {
return __awaiter(this, void 0, void 0, function* () {
const confirm_url = `${API_URL}/deposits/${depositId}/files/confirm`;
const { signedUrl } = uploadSignedUrlResponse, manuscriptImportedMetadata = __rest(uploadSignedUrlResponse, ["signedUrl"]);
try {
const confirmResponse = yield axios_1.default.patch(confirm_url, manuscriptImportedMetadata, {
headers: {
'x-api-key': API_KEY, // Use API key for authentication
'x-api-key-user': API_KEY_USER
}
});
return confirmResponse;
}
catch (error) {
console.error('Error confirming the manuscript upload:', error.message);
throw error; // Properly handle the error by throwing
}
});
}
/**
* Handles the full process of importing a deposit based on metadata loaded from a specified directory.
* This function orchestrates several steps to fully integrate a manuscript into the system: it loads metadata,
* creates a deposit, uploads the manuscript to a pre-signed URL, and finally confirms the manuscript upload.
*
* @param directoryPath - Directory with the deposit manuscript and metadata
* @param community - Orvium community where the deposit will be uploaded
* @returns {Promise<void>} - A promise that resolves if the entire deposit process completes successfully.
* @throws {Error} - Throws an error if any step in the deposit, upload, or confirmation process fails.
*/
function importDeposit(directoryPath, community) {
return __awaiter(this, void 0, void 0, function* () {
try {
const metaData = loadJsonFile(directoryPath);
const deposit = {
title: metaData.title,
abstract: metaData.abstract,
community: community,
authors: metaData.authors.map(transformAuthor),
disciplines: metaData.disciplines,
keywords: metaData.keywords,
manuscript: {
filename: metaData.manuscript.filename,
}
};
//Extract the manuscript name and type from metadata.json
const manuscriptName = deposit.manuscript.filename;
// Import the deposit and get the deposit ID
const depositId = yield importSingleDeposit(deposit);
// Getting SignedUrl
const manuscriptPath = path_1.default.join(directoryPath, manuscriptName);
const manuscriptMetadata = yield createManuscriptMetadata(manuscriptPath);
const uploadSignedUrlResponse = yield generateManuscriptUploadUrl(depositId, manuscriptMetadata);
// Upload to signedUrl
const uploadStatus = yield uploadManuscriptToSignedUrl(manuscriptPath, uploadSignedUrlResponse, manuscriptMetadata);
// Confirm and change metadata
yield confirmManuscriptImported(depositId, uploadSignedUrlResponse);
console.log('Deposit imported Successfully: ', deposit.title);
}
catch (error) {
console.error('An error occurred during import or upload:', error);
}
});
}