@tableau/taco-toolkit
Version:
Tableau Connector Toolkit
255 lines • 20.5 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 __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPackedTacoFilename = exports.getTemplateVarValues = exports.getTemplateAndDestPath = exports.writeTacoFile = exports.createFiles = void 0;
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
var logging_1 = require("../utils/logging");
var status_code_1 = require("../../../shared/errors/status-code");
var error_codes_1 = require("../../../shared/errors/error-codes");
var error_source_1 = require("../../../shared/errors/error-source");
var connector_json_1 = require("../utils/connector-json");
var toolkit_paths_1 = require("../utils/toolkit-paths");
var COMMON_TEMPLATES = ['connection-fields.xml', 'connection-metadata.xml'];
var OAUTH_CONNECTOR_TEMPLATES = ['manifest-oauth.xml', 'oauth-config.xml'];
var BASIC_CONNECTOR_TEMPLATES = ['manifest.xml'];
/**
* Create taco xml files according to the information in the connector.json.
* It does not check file existence. It overwrites if any file exists.
*/
function createFiles(connectorJson, templateAndDests) {
return __awaiter(this, void 0, void 0, function () {
var variableValues, result, failures;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
variableValues = getTemplateVarValues(connectorJson);
return [4 /*yield*/, Promise.allSettled(templateAndDests.map(function (_a) {
var templatePath = _a.templatePath, destFilePath = _a.destFilePath;
return writeTacoFile(templatePath, destFilePath, variableValues);
}))];
case 1:
result = _a.sent();
failures = [];
result.forEach(function (result, index) {
var destFilePath = templateAndDests[index].destFilePath;
if (result.status === 'rejected') {
failures.push({
path: destFilePath,
reason: result.reason,
});
}
});
if (failures.length > 0) {
return [2 /*return*/, {
state: 'error',
error: {
statusCode: status_code_1.StatusCode.Cancelled,
errorSource: error_source_1.ErrorSources.Client,
errorCode: error_codes_1.ErrorCodes.EJB55C4V,
message: "Error occurred when creating files: ".concat(failures),
},
}];
}
return [2 /*return*/, { state: 'success' }];
}
});
});
}
exports.createFiles = createFiles;
function writeTacoFile(templatePath, destFilePath, variableValues) {
return __awaiter(this, void 0, void 0, function () {
var templateContent, content;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, fs_1.default.promises.readFile(templatePath, {
encoding: 'utf8',
flag: 'r',
})];
case 1:
templateContent = _a.sent();
content = templateContent.replace(/{{([^}]+)}}/g, function (matched, variable) {
var value = variableValues[variable];
if (value === undefined) {
// Only log error here to let the file generation complete. The validation phase will stop the pack.
logging_1.logger.error("Missing required variable '".concat(variable, "'. Please check if the corresponding config is set in the connection.json"));
return matched;
}
return value;
});
return [2 /*return*/, fs_1.default.promises.writeFile(destFilePath, content, {
encoding: 'utf8',
flag: 'w',
})];
}
});
});
}
exports.writeTacoFile = writeTacoFile;
function getTacoFileTemplates(connectorJson) {
if ((0, connector_json_1.isOauthConnector)(connectorJson)) {
return __spreadArray(__spreadArray([], __read(COMMON_TEMPLATES), false), __read(OAUTH_CONNECTOR_TEMPLATES), false);
}
return __spreadArray(__spreadArray([], __read(COMMON_TEMPLATES), false), __read(BASIC_CONNECTOR_TEMPLATES), false);
}
function getTemplateAndDestPath(tacoRootPath, connectorJson) {
return getTacoFileTemplates(connectorJson).map(function (template) {
return {
templatePath: path_1.default.join(toolkit_paths_1.TEMPLATE_ROOT_PATH, template),
destFilePath: path_1.default.resolve(tacoRootPath, getDestXmlFilename(template)),
};
});
}
exports.getTemplateAndDestPath = getTemplateAndDestPath;
function getDestXmlFilename(templateName) {
if (templateName === 'manifest-oauth.xml') {
return 'manifest.xml';
}
return templateName;
}
function createXmlTag(tagName, value) {
if (!value) {
return '';
}
return "<".concat(tagName, ">").concat(value, "</").concat(tagName, ">");
}
function createXmlTagList(tagName, values, indent) {
if (!values) {
return '';
}
return values.map(function (value) { return "".concat(' '.repeat(indent), "<").concat(tagName, ">").concat(value, "</").concat(tagName, ">\n"); }).join('');
}
function createXmlTagKeyValue(values, indent) {
if (!values) {
return '';
}
return Object.entries(values)
.map(function (_a) {
var _b = __read(_a, 2), key = _b[0], value = _b[1];
return "".concat(' '.repeat(indent * 2), "<entry>\n") + // prettier
"".concat(' '.repeat(indent * 3), "<key>").concat(key, "</key>\n") +
"".concat(' '.repeat(indent * 3), "<value>").concat(value, "</value>\n") +
"".concat(' '.repeat(indent * 2), "</entry>\n");
})
.join('');
}
function getTemplateVarValues(connectorJson) {
var _a;
var isOauth = (0, connector_json_1.isOauthConnector)(connectorJson);
var values = {
// class name is derived from connectorJson.name
CLASS: (0, connector_json_1.getConnectorClassName)(connectorJson),
// By default, connectorJson.name will be used for connector display name,
// while the connector may use displayName property to overwrites it.
NAME: (_a = connectorJson.displayName) !== null && _a !== void 0 ? _a : connectorJson.name,
'PLUGIN-VERSION': connectorJson.version,
'MIN-TAB-VERSION': connectorJson['tableau-version'].min,
// The version is hardcoded to 18.1 as referring to https://tableau.github.io/connector-plugin-sdk/docs/design
'MANEFIEST-VERSION': '18.1',
// The version needs to be hardcoded to 10.0
// refer to https://github.com/tableau/connector-plugin-sdk/blob/master/samples/plugins/postgres_jdbc/manifest.xml
'CONNECTION-CUSTOM-VERSION': '10.0',
'COMP-NAME': connectorJson.vendor.name,
'COMP-URL': connectorJson.vendor['support-link'],
AUTH: isOauth ? 'oauth' : 'auth-none',
};
if (isOauth) {
// At this point, connectorJson should be already validated.
// The type casting should be safe.
var oauthConfig = connectorJson.auth.oauth;
Object.assign(values, {
// values with tag
OAUTH_CONFIG_ID: createXmlTag('oauthConfigId', oauthConfig.oauthConfigId),
CONFIG_LABEL: createXmlTag('configLabel', oauthConfig.configLabel),
AUTH_URI: createXmlTag('authUri', oauthConfig.authUri),
TOKEN_URI: createXmlTag('tokenUri', oauthConfig.tokenUri),
USERINFO_URI: createXmlTag('userInfoUri', oauthConfig.userInfoUri),
CLIENT_ID: createXmlTag('clientIdDesktop', oauthConfig.clientIdDesktop),
CLIENT_SECRET: createXmlTag('clientSecretDesktop', oauthConfig.clientSecretDesktop),
INSTANCEURL_SUFFIX: createXmlTag('instanceUrlSuffix', oauthConfig.instanceUrlSuffix),
INSTANCEURL_VALIDATION_REGEX: createXmlTag('instanceUrlValidationRegex', oauthConfig.instanceUrlValidationRegex),
DEFAULT_INSTANCEURL: createXmlTag('defaultInstanceUrl', oauthConfig.defaultInstanceUrl),
// list values
REDIRECT_URI: createXmlTagList('redirectUrisDesktop', oauthConfig.redirectUrisDesktop, 4),
SCOPES: createXmlTagList('scopes', oauthConfig.scopes, 4),
// key-value values
CAPABILITIES: createXmlTagKeyValue(oauthConfig.capabilities, 4),
ACCESS_MAPS: createXmlTagKeyValue(oauthConfig.accessTokenResponseMaps, 4),
});
}
return values;
}
exports.getTemplateVarValues = getTemplateVarValues;
function getPackedTacoFilename(connectorJson, nameOnly, tacoName) {
var connectorClassName = tacoName ? tacoName : (0, connector_json_1.getConnectorClassName)(connectorJson);
if (nameOnly) {
return "".concat(connectorClassName, ".taco");
}
var version = connectorJson.version.toLowerCase();
return "".concat(connectorClassName, "-").concat(version, ".taco");
}
exports.getPackedTacoFilename = getPackedTacoFilename;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFjby1maWxlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jbGkvdGFjby1jb21tYW5kcy9wYWNrL3RhY28tZmlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDBDQUFtQjtBQUNuQiw4Q0FBdUI7QUFFdkIsNENBQXFEO0FBRXJELGtFQUErRDtBQUUvRCxrRUFBK0Q7QUFDL0Qsb0VBQWtFO0FBRWxFLDBEQUFpRjtBQUNqRix3REFBMkQ7QUFZM0QsSUFBTSxnQkFBZ0IsR0FBRyxDQUFDLHVCQUF1QixFQUFFLHlCQUF5QixDQUFDLENBQUE7QUFFN0UsSUFBTSx5QkFBeUIsR0FBRyxDQUFDLG9CQUFvQixFQUFFLGtCQUFrQixDQUFDLENBQUE7QUFDNUUsSUFBTSx5QkFBeUIsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0FBRWxEOzs7R0FHRztBQUNILFNBQXNCLFdBQVcsQ0FDL0IsYUFBNEIsRUFDNUIsZ0JBQW1DOzs7Ozs7b0JBRTdCLGNBQWMsR0FBRyxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQTtvQkFFM0MscUJBQU0sT0FBTyxDQUFDLFVBQVUsQ0FDckMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFVBQUMsRUFBOEI7Z0NBQTVCLFlBQVksa0JBQUEsRUFBRSxZQUFZLGtCQUFBOzRCQUNoRCxPQUFPLGFBQWEsQ0FBQyxZQUFZLEVBQUUsWUFBWSxFQUFFLGNBQWMsQ0FBQyxDQUFBO3dCQUNsRSxDQUFDLENBQUMsQ0FDSCxFQUFBOztvQkFKSyxNQUFNLEdBQUcsU0FJZDtvQkFFSyxRQUFRLEdBQTBCLEVBQUUsQ0FBQTtvQkFDMUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFDLE1BQU0sRUFBRSxLQUFLO3dCQUNuQixJQUFBLFlBQVksR0FBSyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsYUFB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