1+ import axios from 'axios' ;
2+ import FormData from 'form-data' ; // Need form-data for multipart POST
3+ import { addResource } from '../../index' ;
4+
5+ /**
6+ * Handler for JPL Horizons File API (POST request)
7+ *
8+ * This API provides ephemeris data for solar system objects using a file-based input.
9+ * It accepts the same parameters as the GET version but formats them for file submission.
10+ *
11+ * @param args Request parameters (e.g., COMMAND, START_TIME, STOP_TIME, etc.)
12+ * @returns API response
13+ */
14+ export async function horizonsFileHandler ( args : Record < string , any > ) {
15+ try {
16+ // Base URL for the Horizons File API (POST)
17+ const baseUrl = 'https://ssd.jpl.nasa.gov/api/horizons_file.api' ;
18+
19+ // Format arguments into the key='value' text format for the input file
20+ // DO NOT include format here, it's a separate form field.
21+ const formattedArgs = { ...args } ;
22+ delete formattedArgs . format ; // Remove format if present
23+
24+ let fileContent = '!$$SOF\n' ; // Add !SOF marker
25+ for ( const [ key , value ] of Object . entries ( formattedArgs ) ) {
26+ let formattedValue : string | number ;
27+ const upperKey = key . toUpperCase ( ) ;
28+
29+ // Leave numbers unquoted
30+ if ( typeof value === 'number' ) {
31+ formattedValue = value ;
32+ }
33+ // Quote ALL other values (strings, including YES/NO)
34+ else {
35+ formattedValue = `'${ String ( value ) . replace ( / ' / g, "\'" ) } '` ;
36+ }
37+
38+ fileContent += `${ upperKey } =${ formattedValue } \n` ;
39+ }
40+ fileContent += '!$$EOF\n' ; // Correct !EOF marker
41+
42+ // Create FormData payload
43+ const form = new FormData ( ) ;
44+ // Add format as a separate field
45+ form . append ( 'format' , args . format || 'json' ) ;
46+ // Add the file content under the 'input' field name
47+ form . append ( 'input' , fileContent , {
48+ filename : 'horizons_input.txt' , // Required filename, content doesn't matter
49+ contentType : 'text/plain' ,
50+ } ) ;
51+
52+ // Make the API request using POST with multipart/form-data
53+ const response = await axios . post ( baseUrl , form , {
54+ headers : {
55+ ...form . getHeaders ( ) , // Important for correct boundary
56+ } ,
57+ } ) ;
58+ const data = response . data ; // Assume response is JSON based on 'format=json'
59+
60+ // Create a resource URI that represents this query (similar to GET handler)
61+ let resourceUri = 'jpl://horizons-file' ; // Distinguish from GET
62+ let resourceName = 'JPL Horizons file-based ephemeris data' ;
63+
64+ if ( args . COMMAND ) {
65+ resourceUri += `/object/${ encodeURIComponent ( args . COMMAND ) } ` ;
66+ resourceName = `${ args . COMMAND } ephemeris data (file input)` ;
67+ if ( args . START_TIME && args . STOP_TIME ) {
68+ resourceName += ` (${ args . START_TIME } to ${ args . STOP_TIME } )` ;
69+ }
70+ }
71+
72+ // Add response to resources
73+ addResource ( resourceUri , {
74+ name : resourceName ,
75+ mimeType : "application/json" , // Assuming JSON response
76+ text : JSON . stringify ( data , null , 2 )
77+ } ) ;
78+
79+ // Format the response
80+ return {
81+ content : [ {
82+ type : "text" ,
83+ text : JSON . stringify ( data , null , 2 )
84+ } ]
85+ } ;
86+ } catch ( error : any ) {
87+ let errorMessage = `Error accessing JPL Horizons File API: ${ error . message } ` ;
88+ if ( error . response ) {
89+ // Include more detail from the API response if available
90+ errorMessage += `\nStatus: ${ error . response . status } \nData: ${ JSON . stringify ( error . response . data ) } ` ;
91+ }
92+ return {
93+ content : [ {
94+ type : "text" ,
95+ text : errorMessage
96+ } ] ,
97+ isError : true
98+ } ;
99+ }
100+ }
101+
102+ // Export default for dynamic imports in index.ts
103+ export default horizonsFileHandler ;
0 commit comments