UNPKG

@apito-io/js-apito-plugin-sdk

Version:

JavaScript SDK for building Apito HashiCorp plugins

810 lines (735 loc) 22.1 kB
/** * Apito JavaScript Plugin SDK - Helper Functions * * This file contains helper functions for creating GraphQL fields, REST endpoints, * and utility functions similar to the Go SDK. */ // ============================================== // GRAPHQL TYPE CREATION HELPERS // ============================================== /** * Create a scalar type definition * @param {string} scalarType - The scalar type (String, Int, Boolean, Float) * @returns {Object} Scalar type definition */ function createScalarType(scalarType) { return { kind: 'scalar', scalarType: scalarType, name: scalarType }; } /** * Create a non-null type wrapper * @param {Object} ofType - The type to wrap * @returns {Object} Non-null type definition */ function createNonNullType(ofType) { return { kind: 'non_null', ofType: ofType }; } /** * Create a list type wrapper * @param {Object} ofType - The type to wrap * @returns {Object} List type definition */ function createListType(ofType) { return { kind: 'list', ofType: ofType }; } /** * Create an object type definition * @param {string} name - Object type name * @param {Object} fields - Object fields * @returns {Object} Object type definition */ function createObjectType(name, fields) { return { kind: 'object', name: name, fields: fields }; } // ============================================== // GRAPHQL FIELD HELPERS // ============================================== /** * Create a basic GraphQL field * @param {string} fieldType - Field type (String, Int, etc.) * @param {string} description - Field description * @returns {Object} GraphQL field definition */ function Field(fieldType, description) { return { type: createScalarType(fieldType), description: description, args: {} }; } /** * Create a GraphQL field with arguments * @param {string|Object} fieldType - Field type string or existing field object * @param {string|Object} description - Field description or args object (if first param is field object) * @param {Object} [args] - Field arguments (only used if first param is string) * @returns {Object} GraphQL field definition */ function FieldWithArgs(fieldType, description, args) { // Handle two usage patterns: // 1. FieldWithArgs('String', 'description', {args}) // 2. FieldWithArgs(StringField('description'), {args}) if (typeof fieldType === 'string') { // Pattern 1: Traditional usage return { type: createScalarType(fieldType), description: description, args: args || {} }; } else if (typeof fieldType === 'object' && fieldType.type) { // Pattern 2: Field object with args to add return { type: fieldType.type, description: fieldType.description, args: description || {} // description parameter contains args in this pattern }; } else { throw new Error('FieldWithArgs: First parameter must be a string type or field object'); } } /** * Create a String field * @param {string} description - Field description * @returns {Object} String field definition */ function StringField(description) { return Field('String', description); } /** * Create an Int field * @param {string} description - Field description * @returns {Object} Int field definition */ function IntField(description) { return Field('Int', description); } /** * Create a Boolean field * @param {string} description - Field description * @returns {Object} Boolean field definition */ function BooleanField(description) { return Field('Boolean', description); } /** * Create a Float field * @param {string} description - Field description * @returns {Object} Float field definition */ function FloatField(description) { return Field('Float', description); } /** * Create a List field * @param {string|Object} itemType - Type of items in the list (string for scalars, object for custom types) * @param {string} description - Field description * @returns {Object} List field definition */ function ListField(itemType, description) { let listItemType; if (typeof itemType === 'string') { // Handle scalar types like 'String', 'Int', etc. listItemType = createScalarType(itemType); } else if (itemType && itemType.typeName && itemType.fields) { // Handle object types created by NewObjectType - use proper object structure listItemType = { kind: 'object', name: itemType.typeName, fields: itemType.fields }; } else { // Fallback to treating as scalar listItemType = createScalarType(itemType); } return { type: createListType(listItemType), description: description, args: {} }; } /** * Create a Non-null field * @param {string} fieldType - Field type * @param {string} description - Field description * @returns {Object} Non-null field definition */ function NonNullField(fieldType, description) { return { type: createNonNullType(createScalarType(fieldType)), description: description, args: {} }; } /** * Create a Non-null list field * @param {string} itemType - Type of items in the list * @param {string} description - Field description * @returns {Object} Non-null list field definition */ function NonNullListField(itemType, description) { return { type: createNonNullType(createListType(createNonNullType(createScalarType(itemType)))), description: description, args: {} }; } /** * Create an Object field * @param {string} description - Field description * @param {Object} objectType - Object type definition (from NewObjectType) * @returns {Object} Object field definition */ function ObjectField(description, objectType) { let fieldType; if (objectType && objectType.typeName && objectType.fields) { // Handle object types created by NewObjectType - use proper object structure fieldType = { kind: 'object', name: objectType.typeName, fields: objectType.fields }; } else { // Fallback to creating a generic object type fieldType = createObjectType('Object', objectType); } return { type: fieldType, description: description, args: {} }; } /** * Create an Object field with arguments * @param {string} description - Field description * @param {Object} fields - Object fields * @param {Object} args - Field arguments * @returns {Object} Object field definition with arguments */ function ObjectFieldWithArgs(description, fields, args) { return { type: createObjectType('Object', fields), description: description, args: args || {} }; } // ============================================== // GRAPHQL ARGUMENT HELPERS // ============================================== /** * Create a String argument * @param {string} description - Argument description * @returns {Object} String argument definition */ function StringArg(description) { return { type: createScalarType('String'), description: description }; } /** * Create an Int argument * @param {string} description - Argument description * @returns {Object} Int argument definition */ function IntArg(description) { return { type: createScalarType('Int'), description: description }; } /** * Create a Boolean argument * @param {string} description - Argument description * @returns {Object} Boolean argument definition */ function BooleanArg(description) { return { type: createScalarType('Boolean'), description: description }; } /** * Create a Float argument * @param {string} description - Argument description * @returns {Object} Float argument definition */ function FloatArg(description) { return { type: createScalarType('Float'), description: description }; } /** * Create a Non-null argument * @param {string} argType - Argument type * @param {string} description - Argument description * @returns {Object} Non-null argument definition */ function NonNullArg(argType, description) { return { type: createNonNullType(createScalarType(argType)), description: description }; } /** * Create a List argument * @param {string|Object} itemType - Type of items in the list (string for scalars, object for complex types) * @param {string} description - Argument description * @returns {Object} List argument definition */ function ListArg(itemType, description) { let listItemType; if (typeof itemType === 'string') { // Scalar type like 'String', 'Int', etc. listItemType = createScalarType(itemType); } else if (typeof itemType === 'object' && itemType.type) { // Complex type from ObjectArg, StringArg, etc. listItemType = itemType.type; } else { throw new Error('ListArg: First parameter must be a string type or argument object'); } return { type: createListType(listItemType), description: description }; } /** * Create an Object argument * @param {string} description - Argument description * @param {Object} properties - Object properties * @returns {Object} Object argument definition */ function ObjectArg(description, properties) { return { type: 'Object', description: description, properties: properties }; } // ============================================== // OBJECT TYPE BUILDER // ============================================== /** * Object Type Builder class for creating complex object types */ class ObjectTypeBuilder { constructor(typeName, description) { this.definition = { typeName: typeName, description: description, fields: {} }; } /** * Add a String field to the object type * @param {string} name - Field name * @param {string} description - Field description * @param {boolean} nullable - Whether the field is nullable * @returns {ObjectTypeBuilder} Builder instance for chaining */ addStringField(name, description, nullable = true) { this.definition.fields[name] = { type: nullable ? createScalarType('String') : createNonNullType(createScalarType('String')), description: description }; return this; } /** * Add an Int field to the object type * @param {string} name - Field name * @param {string} description - Field description * @param {boolean} nullable - Whether the field is nullable * @returns {ObjectTypeBuilder} Builder instance for chaining */ addIntField(name, description, nullable = true) { this.definition.fields[name] = { type: nullable ? createScalarType('Int') : createNonNullType(createScalarType('Int')), description: description }; return this; } /** * Add a Boolean field to the object type * @param {string} name - Field name * @param {string} description - Field description * @param {boolean} nullable - Whether the field is nullable * @returns {ObjectTypeBuilder} Builder instance for chaining */ addBooleanField(name, description, nullable = true) { this.definition.fields[name] = { type: nullable ? createScalarType('Boolean') : createNonNullType(createScalarType('Boolean')), description: description }; return this; } /** * Add a Float field to the object type * @param {string} name - Field name * @param {string} description - Field description * @param {boolean} nullable - Whether the field is nullable * @returns {ObjectTypeBuilder} Builder instance for chaining */ addFloatField(name, description, nullable = true) { this.definition.fields[name] = { type: nullable ? createScalarType('Float') : createNonNullType(createScalarType('Float')), description: description }; return this; } /** * Build and return the object type definition * @returns {Object} Complete object type definition */ build() { return this.definition; } } /** * Create a new object type builder * @param {string} typeName - Object type name * @param {string} description - Object type description * @returns {ObjectTypeBuilder} New object type builder */ function NewObjectType(typeName, description) { return new ObjectTypeBuilder(typeName, description); } // ============================================== // REST API ENDPOINT BUILDERS // ============================================== /** * REST Endpoint Builder class */ class RESTEndpointBuilder { constructor(method, path, description) { this.endpoint = { method: method.toUpperCase(), path: path, description: description, schema: {} }; } /** * Add request schema to the endpoint * @param {Object} schema - Request schema * @returns {RESTEndpointBuilder} Builder instance for chaining */ withRequestSchema(schema) { this.endpoint.schema.request = schema; return this; } /** * Add response schema to the endpoint * @param {Object} schema - Response schema * @returns {RESTEndpointBuilder} Builder instance for chaining */ withResponseSchema(schema) { this.endpoint.schema.response = schema; return this; } /** * Build and return the endpoint definition * @returns {Object} Complete endpoint definition */ build() { return this.endpoint; } } /** * Create a GET endpoint builder * @param {string} path - Endpoint path * @param {string} description - Endpoint description * @returns {RESTEndpointBuilder} GET endpoint builder */ function GETEndpoint(path, description) { return new RESTEndpointBuilder('GET', path, description); } /** * Create a POST endpoint builder * @param {string} path - Endpoint path * @param {string} description - Endpoint description * @returns {RESTEndpointBuilder} POST endpoint builder */ function POSTEndpoint(path, description) { return new RESTEndpointBuilder('POST', path, description); } /** * Create a PUT endpoint builder * @param {string} path - Endpoint path * @param {string} description - Endpoint description * @returns {RESTEndpointBuilder} PUT endpoint builder */ function PUTEndpoint(path, description) { return new RESTEndpointBuilder('PUT', path, description); } /** * Create a DELETE endpoint builder * @param {string} path - Endpoint path * @param {string} description - Endpoint description * @returns {RESTEndpointBuilder} DELETE endpoint builder */ function DELETEEndpoint(path, description) { return new RESTEndpointBuilder('DELETE', path, description); } /** * Create a PATCH endpoint builder * @param {string} path - Endpoint path * @param {string} description - Endpoint description * @returns {RESTEndpointBuilder} PATCH endpoint builder */ function PATCHEndpoint(path, description) { return new RESTEndpointBuilder('PATCH', path, description); } // ============================================== // REST SCHEMA HELPERS // ============================================== /** * Create an object schema * @param {Object} properties - Schema properties * @returns {Object} Object schema definition */ function ObjectSchema(properties) { return { type: 'object', properties: properties }; } /** * Create an array schema * @param {Object} itemSchema - Schema for array items * @returns {Object} Array schema definition */ function ArraySchema(itemSchema) { return { type: 'array', items: itemSchema }; } /** * Create a string schema * @param {string} description - Schema description * @returns {Object} String schema definition */ function StringSchema(description) { return { type: 'string', description: description }; } /** * Create an integer schema * @param {string} description - Schema description * @returns {Object} Integer schema definition */ function IntegerSchema(description) { return { type: 'integer', description: description }; } /** * Create a boolean schema * @param {string} description - Schema description * @returns {Object} Boolean schema definition */ function BooleanSchema(description) { return { type: 'boolean', description: description }; } /** * Create a number schema * @param {string} description - Schema description * @returns {Object} Number schema definition */ function NumberSchema(description) { return { type: 'number', description: description }; } // ============================================== // UTILITY FUNCTIONS // ============================================== /** * Extract string argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {string} defaultValue - Default value if not found * @returns {string} Argument value or default */ function getStringArg(args, key, defaultValue = '') { const value = args[key]; return typeof value === 'string' ? value : defaultValue; } /** * Extract integer argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {number} defaultValue - Default value if not found * @returns {number} Argument value or default */ function getIntArg(args, key, defaultValue = 0) { const value = args[key]; if (typeof value === 'number') { return Math.floor(value); } else if (typeof value === 'string') { const parsed = parseInt(value, 10); return !isNaN(parsed) ? parsed : defaultValue; } return defaultValue; } /** * Extract boolean argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {boolean} defaultValue - Default value if not found * @returns {boolean} Argument value or default */ function getBoolArg(args, key, defaultValue = false) { const value = args[key]; return typeof value === 'boolean' ? value : defaultValue; } /** * Extract float argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {number} defaultValue - Default value if not found * @returns {number} Argument value or default */ function getFloatArg(args, key, defaultValue = 0.0) { const value = args[key]; if (typeof value === 'number') { return value; } else if (typeof value === 'string') { const parsed = parseFloat(value); return !isNaN(parsed) ? parsed : defaultValue; } return defaultValue; } /** * Extract object argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {Object} defaultValue - Default value if not found * @returns {Object} Argument value or default */ function getObjectArg(args, key, defaultValue = {}) { const value = args[key]; return typeof value === 'object' && value !== null ? value : defaultValue; } /** * Extract array argument with default value * @param {Object} args - Arguments object * @param {string} key - Argument key * @param {Array} defaultValue - Default value if not found * @returns {Array} Argument value or default */ function getArrayArg(args, key, defaultValue = []) { const value = args[key]; return Array.isArray(value) ? value : defaultValue; } /** * Log REST arguments for debugging * @param {string} handlerName - Handler name * @param {Object} args - Arguments object */ function logRESTArgs(handlerName, args) { console.error(`SDK: [${handlerName}] REST Args:`, JSON.stringify(args, null, 2)); } /** * Get path parameter from REST arguments * @param {Object} args - Arguments object * @param {string} param - Parameter name (e.g., ":id") * @returns {string} Parameter value or empty string */ function getPathParam(args, param) { return getStringArg(args, param, ''); } /** * Get query parameter from REST arguments * @param {Object} args - Arguments object * @param {string} param - Parameter name * @returns {string} Parameter value or empty string */ function getQueryParam(args, param) { return getStringArg(args, `query_${param}`, ''); } /** * Get body parameter from REST arguments * @param {Object} args - Arguments object * @param {string} param - Parameter name * @returns {any} Parameter value or undefined */ function getBodyParam(args, param) { const body = getObjectArg(args, 'body', {}); return body[param]; } // Export all helper functions module.exports = { // Type creators createScalarType, createNonNullType, createListType, createObjectType, // Field helpers Field, FieldWithArgs, StringField, IntField, BooleanField, FloatField, ListField, NonNullField, NonNullListField, ObjectField, ObjectFieldWithArgs, // Argument helpers StringArg, IntArg, BooleanArg, FloatArg, NonNullArg, ListArg, ObjectArg, // Object type builder ObjectTypeBuilder, NewObjectType, // REST endpoint builders RESTEndpointBuilder, GETEndpoint, POSTEndpoint, PUTEndpoint, DELETEEndpoint, PATCHEndpoint, // Schema helpers ObjectSchema, ArraySchema, StringSchema, IntegerSchema, BooleanSchema, NumberSchema, // Utility functions getStringArg, getIntArg, getBoolArg, getFloatArg, getObjectArg, getArrayArg, logRESTArgs, getPathParam, getQueryParam, getBodyParam };