fabric-ias
Version:
Node.JS Service for Microsoft Fabric supporting infrastructure as code
172 lines (165 loc) • 5.87 kB
JavaScript
;
const Base = require("./base");
const fs = require('fs');
const {
Blob
} = require('buffer');
const path = require('path');
/**
* @class Libraries
* @classdesc
* Manages custom libraries in a Microsoft Fabric Environment.
* Provides methods to retrieve, upload, delete, and replace libraries in both staging and published environments.
* Extends the Base class for authentication and API request handling.
*
* @extends Base
*
* @property {string} id - The environment ID.
* @property {string} endpoint - The API endpoint for the environment.
* @property {Object} staging - The current staging environment libraries and configuration.
* @property {Object} published - The current published environment libraries and configuration.
*/
class Libraries extends Base {
/**
* Constructs a Libraries instance for a given workspace and environment.
* @param {AzOauth} OAuthHandler - OAuth handler for authentication.
* @param {string} workspace_id - The workspace ID.
* @param {string} environment_id - The environment ID.
*/
constructor(OAuthHandler, handler, workspace_id, environment_id) {
super(OAuthHandler, handler);
this.id = environment_id || '';
this.endpoint = `${this.endpoint}/workspaces/${workspace_id}/environments/${environment_id}`;
this.staging = {};
this.published = {};
}
/**
* Retrieves libraries from Fabric Environment and parses them into a structured format.
* Populates `this.staging` and `this.published` with environment YAML and library lists.
*
* @returns {Promise<Libraries|Error>} Returns this instance with updated properties.
*/
async get() {
const [staging, published] = await Promise.all([this._getLib(true), this._getLib(false)]);
this.staging = {
environment: staging.data.environmentYml,
libraries: staging.data.customLibraries.wheelFiles.concat(staging.data.customLibraries.pyFiles, staging.data.customLibraries.jarFiles, staging.data.customLibraries.rTarFiles)
};
this.published = {
// environment: published.environmentYml,
// libraries: published.customLibraries.wheelFiles.concat(
// published.customLibraries.pyFiles,
// published.customLibraries.jarFiles,
// published.customLibraries.rTarFiles
// )
};
return this;
}
/**
* Internal method to fetch libraries from Fabric Environment.
*
* @param {boolean} staging - If true, fetches staging libraries; otherwise, published libraries.
* @returns {Promise<Object>} The environment data from the API.
* @throws {Error} If the API response is invalid or contains an error.
*/
async _getLib(staging = false) {
const staging_url = staging ? 'staging/' : '';
return await this._get(`/${staging_url}libraries`, {}, function (response) {
if (response.data == undefined) throw Error('Invalid Response: Payload expected to contain libraries');
});
}
/**
* Deletes a file from the Fabric Environment Library (staging).
*
* @param {string} file - The filename to remove.
* @returns {Promise<boolean|Error>} True if deleted, otherwise throws an error.
*/
async delete(file) {
const options = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
await this._delete(`/staging/libraries?libraryToDelete=${file}`, options);
return true;
}
/**
* Uploads a file to the Fabric Environment Library (staging).
*
* @param {string} filePath - The path to the file to upload.
* @returns {Promise<boolean|Error>} True if uploaded, otherwise throws an error.
*/
async upload(filePath) {
const options = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
const form = new FormData();
try {
const fileData = fs.readFileSync(filePath);
const file = new Blob([fileData], {
type: 'application/octet-stream'
});
form.append('file', file, path.basename(filePath));
} catch (error) {
this.parseError(error);
}
await this._post(`/staging/libraries`, form, options);
return true;
}
/**
* Replaces a library in the Fabric Environment Library (staging).
* Deletes all matching libraries and uploads the new file.
* Optionally invokes a callback at each step.
*
* @param {string} library - The library name or pattern to replace.
* @param {string} filePath - The path to the new file.
* @param {function} [callback] - Optional callback for progress updates.
* @returns {Promise<void>}
*/
async replace(library, filePath, callback = null) {
const obj = {};
if (Object.keys(this.staging).length === 0 || Object.keys(this.staging.libraries).length === 0) {
if (callback != undefined) {
callback("getLibrary", {});
}
await this.get();
if (callback != undefined) {
callback("getLibrary", this);
}
}
const regex = new RegExp(`${library}`);
const matches = this.staging.libraries.filter(item => regex.test(item));
if (matches.length !== 0) {
for (const match of matches) {
if (callback != undefined) {
callback("delete", {
file: match,
status: "starting"
});
}
if ((await this.delete(match)) && callback != undefined) {
callback("delete", {
file: match,
status: "deleted"
});
}
}
}
const file = path.basename(filePath);
if (callback != undefined) {
callback("upload", {
file: file,
status: "starting"
});
}
if ((await this.upload(filePath)) && callback != undefined) {
callback("upload", {
file: file,
status: "uploaded"
});
}
}
}
module.exports = Libraries;