adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
234 lines (233 loc) • 8.29 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OperationParser = void 0;
const common_1 = require("../common/common");
/**
* Generates parameters for TypeScript functions from an OpenAPI operation.
*
* This class processes an OpenApiOperation object and provides helper methods
* to extract information needed to generate TypeScript function declarations,
* docstrings, signatures, and JSON schemas. It handles parameter processing,
* name deduplication, and type hint generation.
*/
class OperationParser {
/**
* Initializes the OperationParser with an OpenApiOperation.
*
* @param operation The OpenApiOperation object or a dictionary to process
* @param shouldParse Whether to parse the operation during initialization
*/
constructor(operation, shouldParse = true) {
/**
* Parameters for the operation
*/
this.params = [];
/**
* Return value for the operation
*/
this.returnValue = null;
if (typeof operation === 'string') {
this.operation = JSON.parse(operation);
}
else {
this.operation = operation;
}
if (shouldParse) {
this._processOperationParameters();
this._processRequestBody();
this._processReturnValue();
this._dedupeParamNames();
}
}
/**
* Creates an OperationParser with pre-defined params and return value
*
* @param operation The OpenApiOperation object
* @param params Parameters for the operation
* @param returnValue Return value for the operation
* @returns A new OperationParser instance
*/
static load(operation, params, returnValue = null) {
const parser = new OperationParser(operation, false);
parser.params = params;
parser.returnValue = returnValue;
return parser;
}
/**
* Processes parameters from the OpenAPI operation
*/
_processOperationParameters() {
const parameters = this.operation.parameters || [];
for (const param of parameters) {
const originalName = param.name;
const description = param.description || '';
const location = param.in || '';
const schema = param.schema || {};
// Preserve description in schema if not already present
if (schema.description === undefined && description !== '') {
schema.description = description;
}
const required = param.required;
this.params.push(new common_1.ApiParameter(originalName, location, schema, description, '', required));
}
}
/**
* Processes the request body from the OpenAPI operation
*/
_processRequestBody() {
const requestBody = this.operation.requestBody;
if (!requestBody) {
return;
}
const content = requestBody.content || {};
if (Object.keys(content).length === 0) {
return;
}
// Process first mime type only
const firstMimeType = Object.keys(content)[0];
const mediaTypeObject = content[firstMimeType];
const schema = mediaTypeObject.schema || {};
const description = requestBody.description || '';
const required = requestBody.required;
if (schema.type === 'object') {
const properties = schema.properties || {};
// For objects, extract each property as a parameter
Object.entries(properties).forEach(([propName, propDetails]) => {
const propRequired = Array.isArray(schema.required) && schema.required.includes(propName);
this.params.push(new common_1.ApiParameter(propName, 'body', propDetails, propDetails.description || '', '', propRequired));
});
}
else if (schema.type === 'array') {
this.params.push(new common_1.ApiParameter('array', 'body', schema, description, '', required));
}
else {
// Empty name for unnamed body param
this.params.push(new common_1.ApiParameter('', 'body', schema, description, '', required));
}
}
/**
* Deduplicates parameter names to avoid conflicts
*/
_dedupeParamNames() {
const paramsCnt = {};
for (const param of this.params) {
const name = param.pyName;
if (!(name in paramsCnt)) {
paramsCnt[name] = 0;
}
else {
paramsCnt[name]++;
param.pyName = `${name}_${paramsCnt[name] - 1}`;
}
}
}
/**
* Processes the return value from the OpenAPI operation
*/
_processReturnValue() {
const responses = this.operation.responses || {};
// Default to Any if no 2xx response or if schema is missing
let returnSchema = { type: 'any' };
// Take the 20x response with the smallest response code
const validCodes = Object.keys(responses).filter(code => code.startsWith('2'));
const min20xStatusCode = validCodes.length > 0 ?
validCodes.sort()[0] : null;
if (min20xStatusCode && responses[min20xStatusCode].content) {
const content = responses[min20xStatusCode].content;
for (const mimeType in content) {
if (content[mimeType].schema) {
returnSchema = content[mimeType].schema;
break;
}
}
}
this.returnValue = new common_1.ApiParameter('', '', returnSchema, '');
}
/**
* Returns the generated function name
* @returns The function name
*/
getFunctionName() {
const operationId = this.operation.operationId;
if (!operationId) {
throw new Error('Operation ID is missing');
}
return (0, common_1.toSnakeCase)(operationId).substring(0, 60);
}
/**
* Returns the return type hint string (like 'string', 'number', etc.)
* @returns The return type hint
*/
getReturnTypeHint() {
return this.returnValue ? this.returnValue.typeHint : 'any';
}
/**
* Returns the return type value
* @returns The return type value
*/
getReturnTypeValue() {
return this.returnValue ? this.returnValue.typeValue : Object;
}
/**
* Returns the list of Parameter objects
* @returns The parameters
*/
getParameters() {
return this.params;
}
/**
* Returns the return value Parameter object
* @returns The return value
*/
getReturnValue() {
return this.returnValue;
}
/**
* Returns the name of the auth scheme for this operation from the spec
* @returns The auth scheme name
*/
getAuthSchemeName() {
if (this.operation.security && this.operation.security.length > 0) {
const schemeNames = Object.keys(this.operation.security[0]);
if (schemeNames.length > 0) {
return schemeNames[0];
}
}
return '';
}
/**
* Returns the generated JSDoc string
* @returns The JSDoc string
*/
getJSDocString() {
const jsDocParams = this.params.map(param => param.toJSDocString());
const jsDocDescription = this.operation.summary || this.operation.description || '';
const jsDocReturn = common_1.JsDocHelper.generateReturnDoc(this.operation.responses || {});
return `/**
* ${jsDocDescription}
*
${jsDocParams.map(param => ` * ${param}`).join('\n')}
*
* ${jsDocReturn}
*/`;
}
/**
* Returns the JSON schema for the function arguments
* @returns The JSON schema
*/
getJsonSchema() {
const properties = {};
for (const p of this.params) {
properties[p.pyName] = p.paramSchema;
}
return {
properties,
required: this.params
.filter(p => p.required !== false)
.map(p => p.pyName),
title: `${this.operation.operationId || 'unnamed'}_Arguments`,
type: 'object'
};
}
}
exports.OperationParser = OperationParser;