@apistudio/apim-cli
Version:
CLI for API Management Products
760 lines (751 loc) • 28.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/lwgw-transformer-orchestrator.impl.ts
import { CoreTransformOrchestrator, ConfigLoader } from "@apic/smith-transformer";
// src/lwgw-post-transformer.impl.ts
import { TransformerError } from "@apic/smith-transformer";
var LwgwPostTransformer = class {
static {
__name(this, "LwgwPostTransformer");
}
/**
* Organize assets into the assembly structure based on policy sequence
* @param assets Array of transformed assets
* @param allAssets Assets belonging to the api
* @returns Assembly structure with execute, catch, and finally sections
*/
organizeAssets(assets, allAssets) {
if (!allAssets || allAssets.length === 0) {
return {};
}
const apiAsset = allAssets.find((asset) => asset.kind && asset.kind.toLowerCase() === "api");
if (!apiAsset) {
return {};
}
const policySequenceRef = this.findPolicySequenceRef(apiAsset);
if (!policySequenceRef) {
return {};
}
const policySequenceAsset = this.findAssetByRefFromStudioAssets(policySequenceRef, allAssets);
if (!policySequenceAsset) {
return {};
}
const result = {};
const mainPolicyRefs = this.getMainPolicyRefs(policySequenceAsset);
if (mainPolicyRefs && mainPolicyRefs.length > 0) {
result.execute = this.orderAssetsByPolicyRefs(mainPolicyRefs, assets);
}
const errorSections = this.getErrorSections(policySequenceAsset);
if (errorSections && errorSections.length > 0) {
result.catch = this.processCatchSections(errorSections, assets);
}
const finallyPolicyRefs = this.getFinallyPolicyRefs(policySequenceAsset);
if (finallyPolicyRefs && finallyPolicyRefs.length > 0) {
result.finally = this.orderAssetsByPolicyRefs(finallyPolicyRefs, assets);
}
if (Object.keys(result).length === 0 || result.execute && result.execute.length === 0 && !result.catch && !result.finally) {
return {};
}
return result;
}
/**
* Process a single asset to handle $ref properties at any level
* @param asset The asset to process
* @param allAssets All available wrapped assets
* @param refTracker Set to track references and prevent circular dependencies
* @returns Processed asset
*/
processAssetWithRef(asset, allAssets, refTracker = /* @__PURE__ */ new Set()) {
if (!asset || typeof asset !== "object") {
return asset;
}
if (Array.isArray(asset)) {
const processedArray = [];
for (const item of asset) {
const isNestedArray = Array.isArray(item);
const processed = this.processAssetWithRef(item, allAssets, refTracker);
if (Array.isArray(processed) && !isNestedArray) {
processedArray.push(...processed);
} else {
processedArray.push(processed);
}
}
return processedArray;
}
const result = { ...asset };
for (const key in result) {
const value = result[key];
if (key === "$ref" && typeof value === "string") {
if (Object.keys(result).length === 1) {
if (refTracker.has(value)) {
throw new TransformerError(`Circular dependency detected for reference: ${value}`, "Post transformation");
}
const refAsset = this.findAssetByRef(value, allAssets);
if (refAsset && refAsset.outputAsset) {
refTracker.add(value);
const processedAsset = this.processAssetWithRef(refAsset.outputAsset, allAssets, refTracker);
refTracker.delete(value);
return processedAsset;
}
}
} else if (typeof value === "object" && value !== null) {
result[key] = this.processAssetWithRef(value, allAssets, refTracker);
}
}
this.processDeepRefStructures(result, allAssets, refTracker);
return result;
}
/**
* Process deep reference structures in an object
* @param obj The object to process
* @param allAssets All available wrapped assets
* @param refTracker Set to track references and prevent circular dependencies
*/
processDeepRefStructures(obj, allAssets, refTracker) {
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
return;
}
this.traverseAndReplaceRefs(obj, allAssets, refTracker);
}
/**
* Traverse an object and replace any $ref properties with the actual referenced assets
* @param obj The object to traverse
* @param allAssets All available wrapped assets
* @param refTracker Set to track references and prevent circular dependencies
*/
traverseAndReplaceRefs(obj, allAssets, refTracker) {
if (!obj || typeof obj !== "object") {
return;
}
if (Array.isArray(obj)) {
let i = 0;
while (i < obj.length) {
const item = obj[i];
if (item && typeof item === "object") {
if (item.$ref && Object.keys(item).length === 1) {
const refValue = item.$ref;
if (refTracker.has(refValue)) {
throw new TransformerError(`Circular dependency detected for reference: ${refValue}`, "Post transformation");
}
const refAsset = this.findAssetByRef(refValue, allAssets);
if (refAsset && refAsset.outputAsset) {
refTracker.add(refValue);
const processedAsset = this.processAssetWithRef(refAsset.outputAsset, allAssets, refTracker);
refTracker.delete(refValue);
if (Array.isArray(processedAsset)) {
obj.splice(i, 1, ...processedAsset);
i += processedAsset.length;
} else {
obj[i] = processedAsset;
i++;
}
} else {
i++;
}
} else {
this.traverseAndReplaceRefs(item, allAssets, refTracker);
i++;
}
} else {
i++;
}
}
return;
}
for (const key in obj) {
const value = obj[key];
if (key === "$ref" && typeof value === "string") {
const parent = obj;
const refValue = value;
if (refTracker.has(refValue)) {
throw new TransformerError(`Circular dependency detected for reference: ${refValue}`, "Post transformation");
}
const refAsset = this.findAssetByRef(refValue, allAssets);
if (refAsset && refAsset.outputAsset) {
delete parent.$ref;
refTracker.add(refValue);
const processedAsset = this.processAssetWithRef(refAsset.outputAsset, allAssets, refTracker);
refTracker.delete(refValue);
if (typeof processedAsset === "object" && !Array.isArray(processedAsset)) {
Object.assign(parent, processedAsset);
} else if (Array.isArray(processedAsset)) {
if (processedAsset.length === 1) {
const singleItem = processedAsset[0];
if (typeof singleItem === "object" && !Array.isArray(singleItem)) {
Object.assign(parent, singleItem);
} else {
parent.value = singleItem;
}
} else {
parent.items = processedAsset;
}
} else {
parent.value = processedAsset;
}
}
} else if (typeof value === "object" && value !== null) {
this.traverseAndReplaceRefs(value, allAssets, refTracker);
}
}
}
/**
* Find the policy sequence reference in the API asset
* @param apiAsset The API asset
* @returns The policy sequence reference or undefined
*/
findPolicySequenceRef(apiAsset) {
if (apiAsset.spec && apiAsset.spec["policy-sequence"] && apiAsset.spec["policy-sequence"].$ref) {
return apiAsset.spec["policy-sequence"].$ref;
}
if (apiAsset.spec && apiAsset.spec["policy-sequence"] && Array.isArray(apiAsset.spec["policy-sequence"]) && apiAsset.spec["policy-sequence"][0].$ref) {
return apiAsset.spec["policy-sequence"][0].$ref;
}
return this.findRefByKind(apiAsset, "FreeFlowPolicySequence");
}
/**
* Find a reference to an asset of a specific kind in an object
* @param obj The object to search in
* @param kind The kind to look for
* @returns The reference or undefined
*/
findRefByKind(obj, kind) {
if (!obj || typeof obj !== "object") {
return void 0;
}
if (Array.isArray(obj)) {
for (const item of obj) {
const result = this.findRefByKind(item, kind);
if (result)
return result;
}
return void 0;
}
if (obj.$ref && obj.kind && obj.kind.toLowerCase() === kind.toLowerCase()) {
return obj.$ref;
}
for (const key in obj) {
if (typeof obj[key] === "object") {
const result = this.findRefByKind(obj[key], kind);
if (result)
return result;
}
}
return void 0;
}
/**
* Find an asset by reference from StudioAssets
* @param ref Reference string in format "namespace:name:version" or "kind:name:version"
* @param assets All available StudioAssets
* @returns The matching asset or undefined
*/
findAssetByRefFromStudioAssets(ref, assets) {
const parts = ref.split(":");
if (parts.length < 2) {
return void 0;
}
const isKindFormat = parts[0].toLowerCase() === "policy" || parts[0].toLowerCase() === "policysequence" || parts[0].toLowerCase() === "freeflowpolicysequence";
let namespace;
let name;
let version;
if (isKindFormat) {
name = parts[1];
version = parts.length > 2 ? parts[2] : "1.0";
return assets.find(
(a) => a.metadata && a.metadata.name === name && a.metadata.version === version
);
} else {
namespace = parts[0];
name = parts[1];
version = parts.length > 2 ? parts[2] : "1.0";
let asset = assets.find(
(a) => a.metadata && a.metadata.name === name && a.metadata.namespace === namespace && a.metadata.version === version
);
if (asset)
return asset;
asset = assets.find(
(a) => a.metadata && a.metadata.name === name && a.metadata.version === version
);
if (asset)
return asset;
asset = assets.find(
(a) => a.metadata && a.metadata.name === name && a.metadata.namespace === namespace
);
if (asset)
return asset;
return assets.find((a) => a.metadata && a.metadata.name === name);
}
}
/**
* Find an asset by reference from WrappedAssets
* @param ref Reference string in format "namespace:name:version" or "kind:name:version"
* @param assets All available WrappedAssets
* @returns The matching asset or undefined
*/
findAssetByRef(ref, assets) {
const parts = ref.split(":");
if (parts.length < 2) {
return void 0;
}
const isKindFormat = parts[0].toLowerCase() === "policy" || parts[0].toLowerCase() === "policysequence" || parts[0].toLowerCase() === "freeflowpolicysequence";
let namespace;
let name;
let version;
if (isKindFormat) {
name = parts[1];
version = parts.length > 2 ? parts[2] : "1.0";
return assets.find(
(a) => a.inputSchema.metadata.name === name && a.inputSchema.metadata.version === version
);
} else {
namespace = parts[0];
name = parts[1];
version = parts.length > 2 ? parts[2] : "1.0";
let asset = assets.find(
(a) => a.inputSchema.metadata.name === name && a.inputSchema.metadata.namespace === namespace && a.inputSchema.metadata.version === version
);
if (asset)
return asset;
asset = assets.find(
(a) => a.inputSchema.metadata.name === name && a.inputSchema.metadata.version === version
);
if (asset)
return asset;
asset = assets.find(
(a) => a.inputSchema.metadata.name === name && a.inputSchema.metadata.namespace === namespace
);
if (asset)
return asset;
return assets.find((a) => a.inputSchema.metadata.name === name);
}
}
/**
* Get ordered policy references from the main section of a policy sequence
* @param policySequenceAsset The policy sequence asset
* @returns Array of policy references in order
*/
getMainPolicyRefs(policySequenceAsset) {
if (!policySequenceAsset.spec || !policySequenceAsset.spec.main || !Array.isArray(policySequenceAsset.spec.main)) {
return [];
}
return policySequenceAsset.spec.main.filter((item) => item && item.$ref).map((item) => item.$ref);
}
/**
* Get ordered policy references from the finally section of a policy sequence
* @param policySequenceAsset The policy sequence asset
* @returns Array of policy references in order
*/
getFinallyPolicyRefs(policySequenceAsset) {
if (!policySequenceAsset.spec || !policySequenceAsset.spec.finally || !Array.isArray(policySequenceAsset.spec.finally)) {
return [];
}
return policySequenceAsset.spec.finally.filter((item) => item && item.$ref).map((item) => item.$ref);
}
/**
* Get error sections from a policy sequence
* @param policySequenceAsset The policy sequence asset
* @returns Array of error sections
*/
getErrorSections(policySequenceAsset) {
if (!policySequenceAsset.spec || !policySequenceAsset.spec.error || !Array.isArray(policySequenceAsset.spec.error)) {
return [];
}
return policySequenceAsset.spec.error;
}
/**
* Process error sections into catch blocks
* @param errorSections Array of error sections
* @param assets All available assets
* @returns Array of catch blocks
*/
processCatchSections(errorSections, assets) {
return errorSections.map((section) => {
const catchBlock = {};
if (section.errorType && Array.isArray(section.errorType)) {
catchBlock.errors = section.errorType;
}
if (section.execute && Array.isArray(section.execute)) {
const policyRefs = section.execute.filter((item) => item && item.$ref).map((item) => item.$ref);
catchBlock.execute = this.orderAssetsByPolicyRefs(policyRefs, assets);
} else {
catchBlock.execute = [];
}
return catchBlock;
});
}
/**
* Order assets according to policy references
* @param policyRefs Array of policy references in order
* @param assets All available assets
* @returns Array of ordered output assets
*/
orderAssetsByPolicyRefs(policyRefs, assets) {
const orderedAssets = [];
const refTracker = /* @__PURE__ */ new Set();
for (const ref of policyRefs) {
const asset = this.findAssetByRef(ref, assets);
if (asset && asset.outputAsset) {
if (Array.isArray(asset.outputAsset)) {
const processedItems = asset.outputAsset.map(
(item) => this.processAssetWithRef(item, assets, refTracker)
).flat();
orderedAssets.push(...processedItems);
} else {
const processedAsset = this.processAssetWithRef(asset.outputAsset, assets, refTracker);
orderedAssets.push(processedAsset);
}
}
}
return orderedAssets;
}
};
// src/lwgw-transformer-orchestrator.impl.ts
import { LWGWRuntimeInventory } from "@apic/lwgw-smith-inventory";
// src/path.util.ts
var _parentDir = null;
async function getParentDir() {
if (_parentDir === null) {
const { fileURLToPath } = await import("url");
const path = await import("path");
const currentFile = fileURLToPath(import.meta.url);
_parentDir = path.resolve(path.dirname(currentFile), "../dist");
}
return _parentDir;
}
__name(getParentDir, "getParentDir");
// src/transformers/countlimit-transformer.ts
import { BaseTransformer, TransformerError as TransformerError2 } from "@apic/smith-transformer";
var CountLimitTransformer = class extends BaseTransformer {
static {
__name(this, "CountLimitTransformer");
}
async transform(wrappedAsset, config) {
try {
const input = wrappedAsset.inputSchema;
if (config.skipTransform === true) {
return wrappedAsset;
}
if (input.kind.toLowerCase() !== "countlimit") {
return super.transform(wrappedAsset, config);
}
const output = {
countLimit: {
limits: []
}
};
output.countLimit.name = input.metadata.name ?? "";
if (input.spec && input.spec.limits && Array.isArray(input.spec.limits)) {
for (const limit of input.spec.limits) {
if (limit.limitDef) {
output.countLimit.limits.push({
inline: limit.limitDef
});
}
}
}
if (input.spec && input.spec.extensions && input.spec.extensions["dp-nano-gateway"] && input.spec.extensions["dp-nano-gateway"].spec && input.spec.extensions["dp-nano-gateway"].spec.limits && Array.isArray(input.spec.extensions["dp-nano-gateway"].spec.limits)) {
for (const limit of input.spec.extensions["dp-nano-gateway"].spec.limits) {
if (limit.globalLimit) {
output.countLimit.limits.push({
ref: limit.globalLimit
});
}
if (limit.aliasLimit) {
output.countLimit.limits.push({
alias: limit.aliasLimit
});
}
}
}
return {
...wrappedAsset,
outputAsset: output
};
} catch (error) {
if (error instanceof TransformerError2) {
throw error;
}
throw new TransformerError2(
`Failed to transform RateLimit asset: ${error instanceof Error ? error.message : String(error)}`,
"ratelimit-transformer"
);
}
}
};
// src/transformers/ratelimit-transformer.ts
import { BaseTransformer as BaseTransformer2, TransformerError as TransformerError3 } from "@apic/smith-transformer";
var RateLimitTransformer = class extends BaseTransformer2 {
static {
__name(this, "RateLimitTransformer");
}
async transform(wrappedAsset, config) {
try {
const input = wrappedAsset.inputSchema;
if (config.skipTransform === true) {
return wrappedAsset;
}
if (input.kind.toLowerCase() !== "ratelimit") {
return super.transform(wrappedAsset, config);
}
const output = {
rateLimit: {
limits: []
}
};
if (input.spec) {
output.rateLimit.target = input.spec.target ?? "all";
}
output.rateLimit.name = input.metadata.name ?? "";
if (input.spec && input.spec.limits && Array.isArray(input.spec.limits)) {
for (const limit of input.spec.limits) {
if (limit.limitDef) {
output.rateLimit.limits.push({
inline: limit.limitDef
});
}
}
}
if (input.spec && input.spec.extensions && input.spec.extensions["dp-nano-gateway"] && input.spec.extensions["dp-nano-gateway"].spec && input.spec.extensions["dp-nano-gateway"].spec.limits && Array.isArray(input.spec.extensions["dp-nano-gateway"].spec.limits)) {
for (const limit of input.spec.extensions["dp-nano-gateway"].spec.limits) {
if (limit.globalLimit) {
output.rateLimit.limits.push({
ref: limit.globalLimit
});
}
if (limit.aliasLimit) {
output.rateLimit.limits.push({
alias: limit.aliasLimit
});
}
}
}
return {
...wrappedAsset,
outputAsset: output
};
} catch (error) {
if (error instanceof TransformerError3) {
throw error;
}
throw new TransformerError3(
`Failed to transform RateLimit asset: ${error instanceof Error ? error.message : String(error)}`,
"ratelimit-transformer"
);
}
}
};
// src/transformers/sql-injection-filter-transformer.ts
import { BaseTransformer as BaseTransformer3, TransformerError as TransformerError4 } from "@apic/smith-transformer";
var SQLInjectionFilterTransformer = class extends BaseTransformer3 {
static {
__name(this, "SQLInjectionFilterTransformer");
}
async transform(wrappedAsset, config) {
try {
const input = wrappedAsset.inputSchema;
if (config.skipTransform === true) {
return wrappedAsset;
}
if (input.kind.toLowerCase() !== "sqlinjectionfilter") {
return super.transform(wrappedAsset, config);
}
const output = {
sqlInjectionFilter: {
filterOn: input.spec?.filterOn || "body",
message: input.spec?.message || "",
injectionRules: []
}
};
if (input.spec && input.spec.injectionRules) {
if (input.spec.injectionRules.predefined && Array.isArray(input.spec.injectionRules.predefined)) {
output.sqlInjectionFilter.injectionRules.push(...input.spec.injectionRules.predefined);
}
if (input.spec.injectionRules.custom && Array.isArray(input.spec.injectionRules.custom)) {
output.sqlInjectionFilter.injectionRules.push(...input.spec.injectionRules.custom);
}
}
return {
...wrappedAsset,
outputAsset: output
};
} catch (error) {
if (error instanceof TransformerError4) {
throw error;
}
throw new TransformerError4(
`Failed to transform SQLInjectionFilter asset: ${error instanceof Error ? error.message : String(error)}`,
"sql-injection-filter-transformer"
);
}
}
};
// src/lwgw-transformer-orchestrator.impl.ts
var LwgwTransformerOrchestrator = class extends CoreTransformOrchestrator {
static {
__name(this, "LwgwTransformerOrchestrator");
}
/**
* Create a new LWGW transform orchestrator
* @param configRegistry Configuration registry
* @param transformerRegistry Transformer registry
*/
constructor(configRegistry, transformerRegistry, configLoader) {
super(configRegistry, transformerRegistry, configLoader, "12.0.0");
this.postTransformer = new LwgwPostTransformer();
this.inventory = new LWGWRuntimeInventory();
}
/**
* Post-transformation hook that organizes assets into execute, catch, and finally sections
* @param transformedAssets Array of transformed assets
* @param resources Resources extracted from the ZIP file
* @param apiName Name of the API being processed
* @param apiMetadata Metadata of the API
* @returns Organized assets in assembly structure
*/
async postTransform(allAssets, transformedAssets, resources, apiName, apiMetadata) {
const postTransformedAssets = this.postTransformer.organizeAssets(transformedAssets, allAssets);
return { assembly: postTransformedAssets };
}
isApiValid(api, relatedAssets) {
for (const asset of relatedAssets) {
if (asset.kind.toLowerCase() === "stagedpolicysequence") {
return false;
}
}
return true;
}
};
async function createLwgwOrchestrator() {
const configLoader = new ConfigLoader(await getParentDir());
const configRegistry = {
getConfigPath: (_sourceVersion, _targetVersion, kind) => {
switch (kind.toLowerCase()) {
case "invoke":
return "./configs/invoke-transform.json";
case "lambda":
return "./configs/lambda-transform.json";
case "loadbalancer":
return "./configs/load-balancer-transform.json";
case "redact":
return "./configs/redact-transform.json";
case "remove":
return "./configs/remove-transform.json";
case "set":
return "./configs/set-transform.json";
case "transform":
return "./configs/transform-transform.json";
case "ratelimit":
return "./configs/rate-limit-transform.json";
case "ratelimitdef":
return "./configs/rate-limit-def-transform.json";
case "countlimit":
return "./configs/count-limit-transform.json";
case "countlimitdef":
return "./configs/count-limit-def-transform.json";
case "validateapispecification":
return "./configs/validate-transform.json";
case "if":
return "./configs/if-transform.json";
case "switch":
return "./configs/switch-transform.json";
case "operationswitch":
return "./configs/operation-switch-transform.json";
case "authorize":
return "./configs/authorize-transform.json";
case "authenticate":
return "./configs/authenticate-transform.json";
case "block":
return "./configs/block-transform.json";
case "extractidentity":
return "./configs/extract-identity-transform.json";
case "handlebarstemplate":
return "./configs/handlebars-template-transform.json";
case "invokeawslambda":
return "./configs/lambda-transform.json";
case "retry":
return "./configs/retry-transform.json";
case "return":
return "./configs/return-transform.json";
case "throw":
return "./configs/throw-transform.json";
case "try":
return "./configs/try-transform.json";
case "sqlinjectionfilter":
return "./configs/sql-injection-filter-transform.json";
case "enforcecircuitbreaker":
return "./configs/enforce-circuit-breaker-transform.json";
case "cache":
return "./configs/cache-transform.json";
case "luascript":
return "./configs/luascript-transform.json";
case "ibmcloudlogin":
return "./configs/ibmcloudlogin-transform.json";
case "parse":
return "./configs/parse-transform.json";
case "setauthorization":
return "./configs/set-authorization-transform.json";
case "invokewatsonxmodels":
return "./configs/invoke-watsonx-models-transform.json";
case "invokewatsonxtokenize":
return "./configs/invoke-watsonx-tokenize-transform.json";
case "invokewatsonxtextgen":
return "./configs/invoke-watsonx-text-gen-transform.json";
case "invokeopenaichatcompletions":
return "./configs/invoke-openai-chat-completions-transform.json";
case "invokeopenaiembeddings":
return "./configs/invoke-openai-embeddings-transform.json";
case "invokeopenaimodelsid":
return "./configs/invoke-openai-models-id-transform.json";
case "invokeopenaimodels":
return "./configs/invoke-openai-models-transform.json";
case "invokeazureopenaichatcompletions":
return "./configs/invoke-azure-openai-chat-completions-transform.json";
case "invokeazureopenaiembeddings":
return "./configs/invoke-azure-openai-embeddings-transform.json";
case "invokeazureopenaimodelsid":
return "./configs/invoke-azure-openai-models-id-transform.json";
case "invokeazureopenaimodels":
return "./configs/invoke-azure-openai-models-transform.json";
case "invokegeminibatchembedcontents":
return "./configs/invoke-gemini-batch-embed-contents-transform.json";
case "invokegeminicounttokens":
return "./configs/invoke-gemini-count-tokens-transform.json";
case "invokegeminiembedcontent":
return "./configs/invoke-gemini-embed-content-transform.json";
case "invokegeminigeneratecontent":
return "./configs/invoke-gemini-generate-content-transform.json";
case "invokegeminimodelsid":
return "./configs/invoke-gemini-models-id-transform.json";
case "invokegeminimodels":
return "./configs/invoke-gemini-models-transform.json";
case "or":
return "./configs/or-transform.json";
}
return "./configs/skip-transform.json";
}
};
const transformerRegistry = {
getTransformer: (name) => {
if (name === "count-limit-custom") {
return new CountLimitTransformer();
}
if (name === "rate-limit-custom") {
return new RateLimitTransformer();
}
if (name === "sqlinjection-custom") {
return new SQLInjectionFilterTransformer();
}
return void 0;
}
};
return new LwgwTransformerOrchestrator(
configRegistry,
transformerRegistry,
configLoader
);
}
__name(createLwgwOrchestrator, "createLwgwOrchestrator");
export {
LwgwTransformerOrchestrator,
createLwgwOrchestrator
};
//# sourceMappingURL=index.js.map