@@ -3,7 +3,15 @@ package mcp
33import (
44 "context"
55 _ "embed"
6+ "fmt"
7+ "io"
8+ "mokapi/js/faker"
9+ "mokapi/providers/openapi"
10+ "mokapi/providers/openapi/schema"
611 "mokapi/runtime"
12+ "mokapi/schema/json/generator"
13+ "net/http"
14+ "net/textproto"
715 "reflect"
816 "slices"
917 "strings"
@@ -122,6 +130,7 @@ type ApiSummary struct {
122130func (m * mokapi ) init (obj * goja.Object ) {
123131 _ = obj .Set ("getApis" , m .getApis )
124132 _ = obj .Set ("getApi" , m .getApi )
133+ _ = obj .Set ("fake" , m .fake )
125134}
126135
127136func (m * mokapi ) getApis () []ApiSummary {
@@ -146,20 +155,30 @@ func (m *mokapi) getApi(name string) any {
146155 for _ , api := range m .app .ListHttp () {
147156 if api .Info .Name == name {
148157 return & OpenAPI {
149- Name : name ,
150- Type : "http" ,
151- info : api ,
158+ Name : name ,
159+ Type : "http" ,
160+ info : api ,
161+ handler : api .Handler (m .app .Monitor .Http , m .app .Engine , m .app .Events ),
152162 }
153163 }
154164 }
155165 return nil
156166}
157167
168+ func (m * mokapi ) fake (v goja.Value ) (any , error ) {
169+ js , err := faker .ToJsonSchema (v , m .vm )
170+ if err != nil {
171+ return nil , err
172+ }
173+ return generator .New (& generator.Request {Schema : js })
174+ }
175+
158176type OpenAPI struct {
159177 Name string `json:"name"`
160178 Type string `json:"type"`
161179
162- info * runtime.HttpInfo
180+ info * runtime.HttpInfo
181+ handler openapi.Handler
163182}
164183
165184type OperationSummary struct {
@@ -169,11 +188,41 @@ type OperationSummary struct {
169188}
170189
171190type OperationDetails struct {
172- OperationId string `json:"operationId"`
173- Method string `json:"method"`
174- Path string `json:"path"`
175- Summary string `json:"summary"`
176- Description string `json:"description"`
191+ OperationId string `json:"operationId"`
192+ Method string `json:"method"`
193+ Path string `json:"path"`
194+ Summary string `json:"summary"`
195+ Description string `json:"description,omitempty"`
196+ Parameters []RequestParameters `json:"parameters,omitempty"`
197+ RequestBody RequestBody `json:"requestBody,omitempty"`
198+
199+ spec * openapi.Operation
200+ handler openapi.Handler
201+ }
202+
203+ type RequestParameters struct {
204+ Name string `json:"name"`
205+ In string `json:"in"`
206+ Required bool `json:"required"`
207+ Schema * schema.Schema
208+ Description string `json:"description,omitempty"`
209+ }
210+
211+ type RequestBody struct {
212+ Description string `json:"description,omitempty"`
213+ Required bool `json:"required"`
214+ Contents []Content `json:"contents"`
215+ }
216+
217+ type Content struct {
218+ ContentType string `json:"contentType"`
219+ Schema * schema.Schema `json:"schema"`
220+ }
221+
222+ type Response struct {
223+ StatusCode int `json:"statusCode"`
224+ Description string `json:"description,omitempty"`
225+ Content []Content `json:"content"`
177226}
178227
179228func (o * OpenAPI ) GetOperations () []OperationSummary {
@@ -215,17 +264,164 @@ func (o *OpenAPI) GetOperationDetails(path, method string) *OperationDetails {
215264 if op == nil {
216265 continue
217266 }
218- return & OperationDetails {
267+ r := & OperationDetails {
219268 OperationId : op .OperationId ,
220269 Method : method ,
221270 Path : p .Value .Path ,
222271 Summary : op .Summary ,
223272 Description : op .Description ,
273+ spec : op ,
274+ handler : o .handler ,
275+ }
276+ for _ , param := range op .Parameters {
277+ if param .Value == nil {
278+ continue
279+ }
280+ r .Parameters = append (r .Parameters , RequestParameters {
281+ Name : param .Value .Name ,
282+ In : param .Value .Type .String (),
283+ Required : param .Value .Required ,
284+ Schema : param .Value .Schema ,
285+ Description : param .Value .Description ,
286+ })
224287 }
288+ if op .RequestBody != nil && op .RequestBody .Value != nil {
289+ r .RequestBody = RequestBody {
290+ Description : op .RequestBody .Value .Description ,
291+ Required : op .RequestBody .Value .Required ,
292+ }
293+ for ct , content := range op .RequestBody .Value .Content {
294+ r .RequestBody .Contents = append (r .RequestBody .Contents , Content {
295+ ContentType : ct ,
296+ Schema : content .Schema ,
297+ })
298+ }
299+ }
300+ return r
225301 }
226302 return nil
227303}
228304
305+ func (op * OperationDetails ) GetResponseSchema (statusCode int ) * Response {
306+ r := op .spec .Responses .GetResponse (statusCode )
307+ if r == nil {
308+ return nil
309+ }
310+ result := & Response {
311+ StatusCode : statusCode ,
312+ Description : r .Description ,
313+ }
314+ for ct , content := range r .Content {
315+ result .Content = append (result .Content , Content {
316+ ContentType : ct ,
317+ Schema : content .Schema ,
318+ })
319+ }
320+ return result
321+ }
322+
323+ type InvokeRequest struct {
324+ Path map [string ]string `json:"path"`
325+ Query map [string ]string `json:"query"`
326+ Header map [string ][]string `json:"header"`
327+ Body string `json:"body"`
328+ }
329+
330+ type InvokeResponse struct {
331+ StatusCode int `json:"statusCode"`
332+ Headers map [string ][]string `json:"headers"`
333+ Body string `json:"body"`
334+ }
335+
336+ func (op * OperationDetails ) Invoke (req InvokeRequest ) (InvokeResponse , error ) {
337+ result := InvokeResponse {Headers : make (map [string ][]string )}
338+
339+ var body io.Reader
340+ if req .Body != "" {
341+ body = strings .NewReader (req .Body )
342+ }
343+
344+ path := op .Path
345+ query := ""
346+ params := append (op .spec .Path .Parameters , op .spec .Parameters ... )
347+ for _ , p := range params {
348+ if p .Value == nil {
349+ continue
350+ }
351+ switch p .Value .Type {
352+ case openapi .ParameterPath :
353+ if req .Path == nil {
354+ return result , fmt .Errorf ("invoke request %s %s failed: missing path parameter '%s'" , op .Method , op .Path , p .Value .Name )
355+ }
356+ val , ok := req .Path [p .Value .Name ]
357+ if ! ok {
358+ return result , fmt .Errorf ("invoke request %s %s failed: missing path parameter '%s'" , op .Method , op .Path , p .Value .Name )
359+ }
360+ path = strings .ReplaceAll (path , fmt .Sprintf ("{%s}" , p .Value .Name ), val )
361+ case openapi .ParameterQuery :
362+ if req .Query == nil && p .Value .Required {
363+ return result , fmt .Errorf ("invoke request %s %s failed: missing query parameter '%s'" , op .Method , op .Path , p .Value .Name )
364+ }
365+ val , ok := req .Query [p .Value .Name ]
366+ if ! ok {
367+ if ! p .Value .Required {
368+ continue
369+ }
370+ return result , fmt .Errorf ("invoke request %s %s failed: missing query parameter '%s'" , op .Method , op .Path , p .Value .Name )
371+ }
372+ if query != "" {
373+ query += "&"
374+ }
375+ query += fmt .Sprintf ("%s=%s" , p .Value .Name , val )
376+ }
377+ }
378+
379+ if query != "" {
380+ path += "?" + query
381+ }
382+
383+ r , err := http .NewRequest (op .Method , path , body )
384+ if err != nil {
385+ return result , fmt .Errorf ("error creating request: %w" , err )
386+ }
387+ for _ , p := range params {
388+ if p .Value == nil || p .Value .Type != openapi .ParameterHeader {
389+ continue
390+ }
391+ if req .Header == nil && p .Value .Required {
392+ return result , fmt .Errorf ("invoke request %s %s failed: missing header parameter '%s'" , op .Method , op .Path , p .Value .Name )
393+ }
394+ val , ok := req .Header [p .Value .Name ]
395+ if ! ok {
396+ if ! p .Value .Required {
397+ continue
398+ }
399+ return result , fmt .Errorf ("invoke request %s %s failed: missing header parameter '%s'" , op .Method , op .Path , p .Value .Name )
400+ }
401+ r .Header [textproto .CanonicalMIMEHeaderKey (p .Value .Name )] = val
402+ }
403+
404+ he := op .handler .ServeHTTP (& result , r )
405+ if he != nil {
406+ result .StatusCode = he .StatusCode
407+ result .Body = he .Message
408+ }
409+ return result , nil
410+ }
411+
412+ func (r * InvokeResponse ) Header () http.Header {
413+ return r .Headers
414+ }
415+
416+ func (r * InvokeResponse ) WriteHeader (statusCode int ) {
417+ r .StatusCode = statusCode
418+ }
419+
420+ func (r * InvokeResponse ) Write (body []byte ) (int , error ) {
421+ r .Body = string (body )
422+ return len (body ), nil
423+ }
424+
229425type customFieldNameMapper struct {
230426}
231427
0 commit comments