@@ -125,7 +125,7 @@ type ImportConstraints struct {
125125func DefaultConstraints () * ImportConstraints {
126126 return & ImportConstraints {
127127 MaxDataSizeBytes : 50 * 1024 * 1024 , // 50MB
128- SupportedFormats : []Format {FormatHAR , FormatYAML , FormatJSON , FormatCURL , FormatPostman },
128+ SupportedFormats : []Format {FormatHAR , FormatYAML , FormatJSON , FormatCURL , FormatPostman , FormatOpenAPI },
129129 AllowedMimeTypes : []string {
130130 "application/json" ,
131131 "application/har" ,
@@ -218,6 +218,13 @@ func WithLogger(logger *slog.Logger) ServiceOption {
218218 }
219219}
220220
221+ // WithURLFetcher sets a custom URL fetcher (useful for testing)
222+ func WithURLFetcher (fetcher URLFetcher ) ServiceOption {
223+ return func (s * Service ) {
224+ s .urlFetcher = fetcher
225+ }
226+ }
227+
221228// WithHTTPService sets the HTTP service for the service (required for HAR import overwrite detection)
222229func WithHTTPService (httpService * shttp.HTTPService ) ServiceOption {
223230 return func (s * Service ) {
@@ -231,6 +238,7 @@ type Service struct {
231238 importer Importer
232239 validator Validator
233240 translatorRegistry * TranslatorRegistry
241+ urlFetcher URLFetcher
234242 logger * slog.Logger
235243 timeout time.Duration
236244}
@@ -244,6 +252,7 @@ func NewService(importer Importer, validator Validator, opts ...ServiceOption) *
244252 importer : importer ,
245253 validator : validator ,
246254 translatorRegistry : NewTranslatorRegistry (nil ), // Auto-initialize translator registry without HTTP service (will be overridden if provided)
255+ urlFetcher : NewURLFetcher (),
247256 timeout : 30 * time .Minute , // Default timeout for import processing
248257 logger : slog .Default (), // Default logger
249258 }
@@ -461,7 +470,11 @@ func (s *Service) ImportWithTextData(ctx context.Context, req *ImportRequest) (*
461470 return s .Import (ctx , req )
462471}
463472
464- // ImportUnified processes any supported format with automatic detection
473+ // ImportUnified processes any supported format with automatic detection.
474+ // It handles three input modes:
475+ // 1. Binary data (req.Data) - file uploads (Postman JSON, HAR, etc.)
476+ // 2. Text data (req.TextData) - pasted curl commands, raw JSON/YAML
477+ // 3. URL text (req.TextData is a URL) - fetches content from URL (e.g., swagger.json URL)
465478func (s * Service ) ImportUnified (ctx context.Context , req * ImportRequest ) (* ImportResults , error ) {
466479 s .logger .Debug ("ImportUnified: Starting" , "workspace_id" , req .WorkspaceID )
467480 // Check if context is already cancelled
@@ -490,6 +503,11 @@ func (s *Service) ImportUnified(ctx context.Context, req *ImportRequest) (*Impor
490503 return nil , err // Return the original error for workspace access issues
491504 }
492505
506+ // Resolve input data: handle TextData and URL fetching
507+ if err := s .resolveInputData (ctx , req ); err != nil {
508+ return nil , err
509+ }
510+
493511 s .logger .Debug ("ImportUnified: Translating data" )
494512 // Detect format and translate data
495513 translationResult , err := s .importer .ImportAndStoreUnified (ctx , req .Data , req .WorkspaceID )
@@ -689,16 +707,51 @@ func (s *Service) ImportUnifiedWithTextData(ctx context.Context, req *ImportRequ
689707 "has_text_data" , len (req .TextData ) > 0 ,
690708 "has_binary_data" , len (req .Data ) > 0 )
691709
692- // Convert text data to bytes if provided
693- if len (req .Data ) == 0 && req .TextData != "" {
694- req .Data = []byte (req .TextData )
695- s .logger .Debug ("Converted text data to binary" ,
696- "workspace_id" , req .WorkspaceID ,
697- "original_length" , len (req .TextData ),
698- "converted_length" , len (req .Data ))
710+ return s .ImportUnified (ctx , req )
711+ }
712+
713+ // resolveInputData ensures req.Data is populated for format detection.
714+ // It handles three cases:
715+ // 1. Data already present (file upload) - no action needed
716+ // 2. TextData is a URL - fetch content from the URL
717+ // 3. TextData is raw text (curl, JSON, YAML) - convert to bytes
718+ func (s * Service ) resolveInputData (ctx context.Context , req * ImportRequest ) error {
719+ if len (req .Data ) > 0 {
720+ return nil // Already have binary data from file upload
699721 }
700722
701- return s .ImportUnified (ctx , req )
723+ if req .TextData == "" {
724+ return nil // No text data either (validation should have caught this)
725+ }
726+
727+ // Check if TextData is a URL that we should fetch content from
728+ if IsURL (req .TextData ) {
729+ s .logger .Debug ("TextData is a URL, fetching content" ,
730+ "url" , req .TextData ,
731+ "workspace_id" , req .WorkspaceID )
732+
733+ data , err := s .urlFetcher .Fetch (ctx , req .TextData )
734+ if err != nil {
735+ return fmt .Errorf ("failed to fetch URL %q: %w" , req .TextData , err )
736+ }
737+
738+ req .Data = data
739+ if req .Name == "" {
740+ req .Name = req .TextData
741+ }
742+ s .logger .Debug ("URL content fetched successfully" ,
743+ "url" , req .TextData ,
744+ "data_size" , len (data ))
745+ return nil
746+ }
747+
748+ // TextData is raw text (curl command, JSON, YAML, etc.)
749+ req .Data = []byte (req .TextData )
750+ s .logger .Debug ("Converted text data to binary" ,
751+ "workspace_id" , req .WorkspaceID ,
752+ "data_size" , len (req .Data ))
753+
754+ return nil
702755}
703756
704757// DetectFormat performs format detection on the provided data
0 commit comments