woodwing-assets
Version:
TypeScript client for interacting with the WoodWing Assets Server API
217 lines (216 loc) • 8.77 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssetsServerBase = exports.AssetsError = void 0;
const axios_1 = __importDefault(require("axios"));
const form_data_1 = __importDefault(require("form-data"));
const fs = __importStar(require("fs"));
const https_1 = __importDefault(require("https"));
class AssetsError extends Error {
constructor(code, message) {
super(message);
this.code = code;
this.name = 'AssetsError';
}
}
exports.AssetsError = AssetsError;
class AssetsServerBase {
constructor(config) {
this.tokenResponse = {};
this.config = config;
this.tokenValidity = (config.tokenValidityInMinutes ?? 30) * 60 * 1000;
this.axiosInstance = axios_1.default.create({
baseURL: config.serverUrl,
httpsAgent: new https_1.default.Agent({ rejectUnauthorized: config.rejectUnauthorized }),
});
}
// Generic verbs
get(service, params = {}) {
return this.call(service, 'GET', { params });
}
post(service, data = {}, sendAsJson = false) {
return this.call(service, 'POST', { data }, sendAsJson);
}
put(service, data = {}, sendAsJson = false) {
return this.call(service, 'PUT', { data }, sendAsJson);
}
delete(service, params = {}) {
return this.call(service, 'DELETE', { params });
}
download(service, filePath) {
return this.downloadFile(service, filePath);
}
setToken(TokenResponse) {
this.tokenResponse = TokenResponse;
}
getToken() {
return this.tokenResponse;
}
isTokenValid() {
return (!!this.tokenResponse.authToken &&
!!this.tokenResponse.authTimestamp &&
Date.now() - this.tokenResponse.authTimestamp < this.tokenValidity);
}
// Main wrapper
async call(service, method, options, sendAsJson = true) {
if (!this.isTokenValid()) {
this.tokenResponse = { authToken: undefined, authTimestamp: undefined };
}
if (!this.tokenResponse.authToken) {
await this.authenticate();
}
try {
return await this.callWithAuth(service, method, options, sendAsJson);
}
catch (err) {
if (err instanceof AssetsError && err.code === 401) {
await this.authenticate();
return await this.callWithAuth(service, method, options, sendAsJson);
}
throw err;
}
}
async callWithAuth(service, method, options, sendAsJson) {
const config = {
method,
url: service.startsWith('/') ? service.slice(1) : service,
...options,
headers: {
...options.headers,
Authorization: `Bearer ${this.tokenResponse.authToken ?? 'placeholder'}`,
},
};
if (!sendAsJson && (method === 'POST' || method === 'PUT') && config.data) {
const formData = new form_data_1.default();
for (const key in config.data) {
formData.append(key, config.data[key]);
}
config.data = formData;
config.headers = {
...config.headers,
...formData.getHeaders(),
};
}
let response;
try {
response = await this.axiosInstance.request(config);
}
catch (err) {
if (axios_1.default.isAxiosError(err)) {
const status = err.response?.status ?? 500;
const data = err.response?.data;
// Check if response data includes errorcode/message like a soft error
if (data && typeof data === 'object' && 'errorcode' in data && typeof data.errorcode === 'number') {
const message = data.message || 'Unknown error from Assets Server';
throw new AssetsError(data.errorcode, message);
}
throw new AssetsError(status, err.message || 'HTTP request failed');
}
// Non-Axios errors
throw new AssetsError(500, err instanceof Error ? err.message : 'Unexpected error in callWithAuth');
}
const data = response.data;
// Check for soft error outside the try
if (data && typeof data === 'object' && 'errorcode' in data) {
const code = Number(data.errorcode);
const isValidHttp = Number.isInteger(code) && code >= 100 && code <= 599;
throw new AssetsError(isValidHttp ? code : 500, data.message || 'Unknown error from Assets Server');
}
return data;
}
// Token handshake
async authenticate() {
const form = new form_data_1.default();
form.append('username', this.config.username);
form.append('password', this.config.password);
const response = await this.axiosInstance.post('/services/apilogin', form, {
headers: form.getHeaders(),
});
if (response.data.loginSuccess) {
this.tokenResponse.authToken = response.data.authToken;
this.tokenResponse.authTimestamp = Date.now();
return this.tokenResponse;
}
else {
throw new Error(response.data.loginFaultMessage || 'Login failed');
}
}
// For file streams (download)
async downloadFile(service, filePath) {
// Ensure authentication is valid
if (!this.isTokenValid()) {
this.tokenResponse = { authToken: undefined, authTimestamp: undefined };
}
if (!this.tokenResponse.authToken) {
await this.authenticate();
}
try {
return await this.downloadFileWithAuth(service, filePath);
}
catch (err) {
if (err instanceof AssetsError && err.code === 401) {
await this.authenticate();
return await this.downloadFileWithAuth(service, filePath);
}
throw err;
}
}
async downloadFileWithAuth(service, filePath) {
const response = await this.axiosInstance.get(service, {
headers: { Authorization: `Bearer ${this.tokenResponse.authToken ?? 'placeholder'}` },
responseType: 'stream',
});
const writer = fs.createWriteStream(filePath);
return new Promise((resolve, reject) => {
response.data.pipe(writer);
writer.on('finish', () => resolve(filePath));
writer.on('error', reject);
});
}
}
exports.AssetsServerBase = AssetsServerBase;
// Replacement policies
AssetsServerBase.FOLDER_REPLACE_POLICY_AUTO_RENAME = 'AUTO_RENAME';
AssetsServerBase.FOLDER_REPLACE_POLICY_MERGE = 'MERGE';
AssetsServerBase.FOLDER_REPLACE_POLICY_THROW_EXCEPTION = 'THROW_EXCEPTION';
AssetsServerBase.FILE_REPLACE_POLICY_AUTO_RENAME = 'AUTO_RENAME';
AssetsServerBase.FILE_REPLACE_POLICY_OVERWRITE = 'OVERWRITE';
AssetsServerBase.FILE_REPLACE_POLICY_OVERWRITE_IF_NEWER = 'OVERWRITE_IF_NEWER';
AssetsServerBase.FILE_REPLACE_POLICY_REMOVE_SOURCE = 'REMOVE_SOURCE';
AssetsServerBase.FILE_REPLACE_POLICY_THROW_EXCEPTION = 'THROW_EXCEPTION';
AssetsServerBase.FILE_REPLACE_POLICY_DO_NOTHING = 'DO_NOTHING';