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