8
8
using Microsoft . Extensions . Configuration ;
9
9
using Microsoft . Extensions . Logging ;
10
10
using System . Text . Json ;
11
+ using System . Text . RegularExpressions ;
11
12
using System . Web ;
12
13
13
14
namespace DevProxy . Plugins . RequestLogs ;
@@ -113,7 +114,7 @@ request.Method is null ||
113
114
var rootModel = models . Last ( ) ;
114
115
op . Parameters . Add ( new ( )
115
116
{
116
- Name = ( rootModel . IsArray ? ( await MakeSingular ( rootModel . Name ) ) : rootModel . Name ) . ToCamelCase ( ) ,
117
+ Name = await GetParameterName ( rootModel ) ,
117
118
Value = rootModel . Name ,
118
119
In = ParameterLocation . Body
119
120
} ) ;
@@ -170,7 +171,7 @@ request.Method is null ||
170
171
var rootModel = models . Last ( ) ;
171
172
if ( rootModel . IsArray )
172
173
{
173
- res . BodyType = $ "{ await MakeSingular ( rootModel . Name ) } []";
174
+ res . BodyType = $ "{ rootModel . Name } []";
174
175
op . Name = await GetOperationName ( "list" , url ) ;
175
176
}
176
177
else
@@ -212,6 +213,22 @@ request.Method is null ||
212
213
e . GlobalData [ GeneratedTypeSpecFilesKey ] = generatedTypeSpecFiles ;
213
214
}
214
215
216
+ private async Task < string > GetParameterName ( Model model )
217
+ {
218
+ Logger . LogTrace ( "Entered GetParameterName" ) ;
219
+
220
+ var name = model . IsArray ? SanitizeName ( await MakeSingular ( model . Name ) ) : model . Name ;
221
+ if ( string . IsNullOrEmpty ( name ) )
222
+ {
223
+ name = model . Name ;
224
+ }
225
+
226
+ Logger . LogDebug ( "Parameter name: {name}" , name ) ;
227
+ Logger . LogTrace ( "Left GetParameterName" ) ;
228
+
229
+ return name ;
230
+ }
231
+
215
232
private async Task < TypeSpecFile > GetOrCreateTypeSpecFile ( List < TypeSpecFile > files , Uri url )
216
233
{
217
234
Logger . LogTrace ( "Entered GetOrCreateTypeSpecFile" ) ;
@@ -252,7 +269,11 @@ private string GetRootNamespaceName(Uri url)
252
269
{
253
270
Logger . LogTrace ( "Entered GetRootNamespaceName" ) ;
254
271
255
- var ns = string . Join ( "" , url . Host . Split ( '.' ) . Select ( x => x . ToPascalCase ( ) ) ) ;
272
+ var ns = SanitizeName ( string . Join ( "" , url . Host . Split ( '.' ) . Select ( x => x . ToPascalCase ( ) ) ) ) ;
273
+ if ( string . IsNullOrEmpty ( ns ) )
274
+ {
275
+ ns = GetRandomName ( ) ;
276
+ }
256
277
257
278
Logger . LogDebug ( "Root namespace name: {ns}" , ns ) ;
258
279
Logger . LogTrace ( "Left GetRootNamespaceName" ) ;
@@ -268,14 +289,47 @@ private async Task<string> GetOperationName(string method, Uri url)
268
289
Logger . LogDebug ( "Url: {url}" , url ) ;
269
290
Logger . LogDebug ( "Last non-parametrizable segment: {lastSegment}" , lastSegment ) ;
270
291
271
- var operationName = $ "{ method . ToLowerInvariant ( ) } { ( method == "list" ? lastSegment : await MakeSingular ( lastSegment ) ) . ToPascalCase ( ) } ";
292
+ var name = method == "list" ? lastSegment : await MakeSingular ( lastSegment ) ;
293
+ if ( string . IsNullOrEmpty ( name ) )
294
+ {
295
+ name = lastSegment ;
296
+ }
297
+ name = SanitizeName ( name ) ;
298
+ if ( string . IsNullOrEmpty ( name ) )
299
+ {
300
+ name = SanitizeName ( lastSegment ) ;
301
+ if ( string . IsNullOrEmpty ( name ) )
302
+ {
303
+ name = GetRandomName ( ) ;
304
+ }
305
+ }
306
+
307
+ var operationName = $ "{ method . ToLowerInvariant ( ) } { name . ToPascalCase ( ) } ";
308
+ var sanitizedName = SanitizeName ( operationName ) ;
309
+ if ( ! string . IsNullOrEmpty ( sanitizedName ) )
310
+ {
311
+ Logger . LogDebug ( "Sanitized operation name: {sanitizedName}" , sanitizedName ) ;
312
+ operationName = sanitizedName ;
313
+ }
272
314
273
315
Logger . LogDebug ( "Operation name: {operationName}" , operationName ) ;
274
316
Logger . LogTrace ( "Left GetOperationName" ) ;
275
317
276
318
return operationName ;
277
319
}
278
320
321
+ private string GetRandomName ( )
322
+ {
323
+ Logger . LogTrace ( "Entered GetRandomName" ) ;
324
+
325
+ var name = Guid . NewGuid ( ) . ToString ( "N" ) ;
326
+
327
+ Logger . LogDebug ( "Random name: {name}" , name ) ;
328
+ Logger . LogTrace ( "Left GetRandomName" ) ;
329
+
330
+ return name ;
331
+ }
332
+
279
333
private async Task < string > GetOperationDescription ( string method , Uri url )
280
334
{
281
335
Logger . LogTrace ( "Entered GetOperationDescription" ) ;
@@ -392,7 +446,16 @@ private bool IsParametrizable(string segment)
392
446
}
393
447
else
394
448
{
395
- previousSegment = ( await MakeSingular ( segmentTrimmed ) ) . ToCamelCase ( ) ;
449
+ previousSegment = SanitizeName ( await MakeSingular ( segmentTrimmed ) ) ;
450
+ if ( string . IsNullOrEmpty ( previousSegment ) )
451
+ {
452
+ previousSegment = SanitizeName ( segmentTrimmed ) ;
453
+ if ( previousSegment . Length == 0 )
454
+ {
455
+ previousSegment = GetRandomName ( ) ;
456
+ }
457
+ }
458
+ previousSegment = previousSegment . ToCamelCase ( ) ;
396
459
route . Add ( segmentTrimmed ) ;
397
460
}
398
461
}
@@ -457,12 +520,6 @@ private async Task<string> AddModelFromJsonElement(JsonElement jsonElement, stri
457
520
{
458
521
Logger . LogTrace ( "Entered AddModelFromJsonElement" ) ;
459
522
460
- var model = new Model
461
- {
462
- Name = await MakeSingular ( name ) ,
463
- IsError = isError
464
- } ;
465
-
466
523
switch ( jsonElement . ValueKind )
467
524
{
468
525
case JsonValueKind . String :
@@ -483,6 +540,12 @@ private async Task<string> AddModelFromJsonElement(JsonElement jsonElement, stri
483
540
return "Empty" ;
484
541
}
485
542
543
+ var model = new Model
544
+ {
545
+ Name = await GetModelName ( name ) ,
546
+ IsError = isError
547
+ } ;
548
+
486
549
foreach ( var p in jsonElement . EnumerateObject ( ) )
487
550
{
488
551
var property = new ModelProperty
@@ -495,16 +558,50 @@ private async Task<string> AddModelFromJsonElement(JsonElement jsonElement, stri
495
558
models . Add ( model ) ;
496
559
return model . Name ;
497
560
case JsonValueKind . Array :
498
- await AddModelFromJsonElement ( jsonElement . EnumerateArray ( ) . FirstOrDefault ( ) , name , isError , models ) ;
499
- model . IsArray = true ;
500
- model . Name = name ;
501
- models . Add ( model ) ;
502
- return $ "{ name } []";
561
+ // we need to create a model for each item in the array
562
+ // in case some items have null values or different shapes
563
+ // we'll merge them later
564
+ var modelName = string . Empty ;
565
+ foreach ( var item in jsonElement . EnumerateArray ( ) )
566
+ {
567
+ modelName = await AddModelFromJsonElement ( item , name , isError , models ) ;
568
+ }
569
+ models . Add ( new Model
570
+ {
571
+ Name = modelName ,
572
+ IsError = isError ,
573
+ IsArray = true ,
574
+ } ) ;
575
+ return $ "{ modelName } []";
576
+ case JsonValueKind . Null :
577
+ return "null" ;
503
578
default :
504
579
return string . Empty ;
505
580
}
506
581
}
507
582
583
+ private async Task < string > GetModelName ( string name )
584
+ {
585
+ Logger . LogTrace ( "Entered GetModelName" ) ;
586
+
587
+ var modelName = SanitizeName ( await MakeSingular ( name ) ) ;
588
+ if ( string . IsNullOrEmpty ( modelName ) )
589
+ {
590
+ modelName = SanitizeName ( name ) ;
591
+ if ( string . IsNullOrEmpty ( modelName ) )
592
+ {
593
+ modelName = GetRandomName ( ) ;
594
+ }
595
+ }
596
+
597
+ modelName = modelName . ToPascalCase ( ) ;
598
+
599
+ Logger . LogDebug ( "Model name: {modelName}" , modelName ) ;
600
+ Logger . LogTrace ( "Left GetModelName" ) ;
601
+
602
+ return modelName ;
603
+ }
604
+
508
605
private async Task < string > MakeSingular ( string noun )
509
606
{
510
607
Logger . LogTrace ( "Entered MakeSingular" ) ;
@@ -517,11 +614,26 @@ private async Task<string> MakeSingular(string noun)
517
614
}
518
615
var singular = singularNoun ? . Response ;
519
616
520
- if ( singular is null ||
521
- string . IsNullOrEmpty ( singular ) ||
617
+ if ( string . IsNullOrEmpty ( singular ) ||
522
618
singular . Contains ( ' ' ) )
523
619
{
524
- singular = noun . EndsWith ( 's' ) && ! noun . EndsWith ( "ss" ) ? noun [ 0 ..^ 1 ] : noun ;
620
+ if ( noun . EndsWith ( "ies" ) )
621
+ {
622
+ singular = noun [ 0 ..^ 3 ] + 'y' ;
623
+ }
624
+ else if ( noun . EndsWith ( "es" ) )
625
+ {
626
+ singular = noun [ 0 ..^ 2 ] ;
627
+ }
628
+ else if ( noun . EndsWith ( 's' ) && ! noun . EndsWith ( "ss" ) )
629
+ {
630
+ singular = noun [ 0 ..^ 1 ] ;
631
+ }
632
+ else
633
+ {
634
+ singular = noun ;
635
+ }
636
+
525
637
Logger . LogDebug ( "Failed to get singular form of {noun} from LLM. Using fallback: {singular}" , noun , singular ) ;
526
638
}
527
639
@@ -530,4 +642,16 @@ private async Task<string> MakeSingular(string noun)
530
642
531
643
return singular ;
532
644
}
645
+
646
+ private string SanitizeName ( string name )
647
+ {
648
+ Logger . LogTrace ( "Entered SanitizeName" ) ;
649
+
650
+ var sanitized = Regex . Replace ( name , "[^a-zA-Z0-9_]" , "" ) ;
651
+
652
+ Logger . LogDebug ( "Sanitized name: {name} to: {sanitized}" , name , sanitized ) ;
653
+ Logger . LogTrace ( "Left SanitizeName" ) ;
654
+
655
+ return sanitized ;
656
+ }
533
657
}
0 commit comments