/**
* @file plugins/schema.js
* @copyright @spmhome @_2025
* @author Scott Meesseman @spmeesseman
*//** */
const WpwTaskPlugin = require("./basetask");
const { getScriptBuildAssets, getScriptBuildDependencies } = require("../utils/utils");
/**
* @augments WpwTaskPlugin
* @example Replace 'NewPluginName' with a name that will match the rc setting, e.g. WpwSchemaPlugin would
* have an options key of 'schema' in the rc config file.
*/
class WpwSchemaPlugin extends WpwTaskPlugin
{
/**
* @param {WpwPluginOptions} options
*/
constructor(options)
{
super("schema", { taskHandler: "executeSchemaBuild", ...options });
this.buildOptions = /** @type {WpwBuildOptionsPluginConfig<"schema">} */(this.buildOptions); // reset for typings
}
/**
* @override
*/
static create = WpwSchemaPlugin.wrap.bind(this);
/**
* @override
* @returns {WpwPluginTapOptions<any, any, any>}
*/
onApply()
{
return this._obj_.apply(this.buildOptions.upload ?
{
cleanTypesTemporaryFiles: {
async: true,
hook: "done",
callback: this.uploadJsonSchema.bind(this)
}
} : {}, super.onApply());
}
/**
* @returns {Promise<WpwPluginTaskResult | SpmhError>}
*/
async executeSchemaBuild()
{
try
{ const bo = this.buildOptions,
scripts = bo.scripts,
assets = scripts.assets || { immutable: false, tag: this.optionsKey };
await this.execScriptsAsync(scripts, "");
return {
tag: assets.tag,
immutable: !!assets.immutable,
glob: assets.glob || "*.json",
paths: getScriptBuildAssets(bo.scripts, bo.jsonPath),
srcpaths: getScriptBuildDependencies(this.build, bo.scripts, bo.jsonPath)
};
}
catch (e)
{ this.addMessage(
{ exception: e,
code: this.MsgCode.ERROR_PLUGIN_HOOK_FAILED,
message: "failed to execute 'schema' type build"
}, true);
}
}
/**
* @private
* @param {string} version
* @param {IWpwConfigApiEndpoint} apiEp
* @returns {string}
*/
getPlinkCommand(version, apiEp)
{
const plinkCmds = [
`mkdir -p ${apiEp.path}`,
`mkdir -p ${apiEp.path}/${version}`,
`mkdir -p ${apiEp.path}/${version}/template`,
`mkdir -p ${apiEp.path}/latest`,
`mkdir -p ${apiEp.path}/latest/template`,
`rm -f ${apiEp.path}/${version}/*.schema.json"`,
`rm -f ${apiEp.path}/${version}/template/*.*"`,
`rm -f ${apiEp.path}/latest/*.schema.json"`,
`rm -f ${apiEp.path}/latest/template/*.*"`,
`rm -fr ${apiEp.path}/${version}/template"`,
`rm -fr ${apiEp.path}/latest/template"`
];
return "plink " + [
"-ssh", "-batch", "-pw", apiEp.credential.key, `${apiEp.credential.user}@${apiEp.host}`, plinkCmds.join(";")
].join(" ");
}
/**
* @private
* @param {string} remoteFolder
* @param {IWpwConfigApiEndpoint} apiEp
* @returns {string}
*/
getPscpCommand(remoteFolder, apiEp)
{
const path = this._path_.absPath(this.buildOptions.jsonPath);
return "pscp " + [
"-pw", apiEp.credential.key, "-q", this._path_.joinPath(path, "*.json"),
`${apiEp.credential.user}@${apiEp.host}:"${apiEp.path}/${remoteFolder}"`
].join(" ");
}
/**
* @returns {Promise<void>}
*/
async uploadJsonSchema()
{
const b = this.build,
logger = this.logger,
lPad = "", logPad = " ",
bo = this.buildOptions, apiEp = bo.upload,
version = `v${b.pkgJson.version}`,
execOptions = { cwd: b.getContextPath() },
projSlug = b.wrapper.settings.project.slug || b.pkgJson.scopedName.name;
apiEp.path ||= `/var/www/spmhome.io/product/${projSlug}/schema`;
logger.write("upload json schema assets", 1, lPad);
logger.value(" host", apiEp.host, 2, lPad);
logger.value(" method", apiEp.method, 2, lPad);
logger.value(" protocol", apiEp.protocol, 2, lPad);
logger.value(" local json schema directory", bo.jsonPath, 2, lPad);
logger.value(" remote json schema base directory", apiEp.path, 2, lPad);
if (bo.upload.protocol === "pscp")
{
const execOpts = { logPad, execOptions };
try
{ logger.write(" create / clean json schema remote directories", 1, lPad);
let command = this.getPlinkCommand(version, apiEp);
let result = await this.exec(command, "plink", true, execOpts);
if (result.code !== 0) {
throw new Error("failed to prepre remote upload directory [plink]");
}
logger.write(` upload json schema assets to remote directory '~/${version}'`, 1, lPad);
command = this.getPscpCommand(version, apiEp)
result = await this.exec(command, "pscp", true, execOpts);
if (result.code !== 0) {
throw new Error(`failed to upload json schema assets to directory '.../v${version}'`);
}
logger.write(" upload json schema assets to remote directory '~/latest'", 1, lPad);
command = this.getPscpCommand("latest", apiEp)
result = await this.exec(command, "pscp", true, execOpts);
if (result.code !== 0) {
throw new Error("failed to upload json schema assets to directory '/latest'");
}
logger.write("successfully uploaded json schema assets", 1, lPad);
}
catch (e)
{ this.addMessage(
{ exception: e, code: this.MsgCode.ERROR_UPLOAD_FAILED,
message: "pscp|plink execution returned failure status"
});
}
}
else
{ this.addMessage(
{ code: this.MsgCode.ERROR_NOT_IMPLEMENTED,
message: "failed to upload json schema assets",
detail: `specified protocol '${bo.upload.protocol}' not yet supported`
}, true);
}
}
}
module.exports = WpwSchemaPlugin.create;