UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

760 lines (751 loc) 28.5 kB
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