/
SwaggerConfig.cs
970 lines (879 loc) · 46.7 KB
/
SwaggerConfig.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
using Newtonsoft.Json;
using Swagger.Net;
using Swagger.Net.Application;
using Swagger_Test;
using Swagger_Test.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Caching;
using System.Web;
using System.Web.Http;
using System.Web.Http.Description;
using System.Yaml.Serialization;
using static Swagger_Test.Controllers.TestEnumController;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace Swagger_Test
{
public class SwaggerConfig
{
public static Assembly ThisAssembly { get { return typeof(SwaggerConfig).Assembly; } }
public static Action<SwaggerDocsConfig> SwaggerDocConf
{
get
{
return c =>
{
// By default, the service root url is inferred from the request used to access the docs.
// However, there may be situations (e.g. proxy and load-balanced environments) where this does not
// resolve correctly. You can workaround this by providing your own code to determine the root URL.
//
//c.RootUrl(req => GetRootUrlFromAppConfig());
// If schemes are not explicitly provided in a Swagger 2.0 document, then the scheme used to access
// the docs is taken as the default. If your API supports multiple schemes and you want to be explicit
// about them, you can use the "Schemes" option as shown below.
//
//c.Schemes(new[] { "http", "https" });
// Use "SingleApiVersion" to describe a single version API. Swagger 2.0 includes an "Info" object to
// hold additional metadata for an API. Version and title are required but you can also provide
// additional fields by chaining methods off SingleApiVersion.
//
c.SingleApiVersion("v1", "Swagger_Test");
// Taking to long to load the swagger docs? Enable this option to start caching it
//
//c.AllowCachingSwaggerDoc();
// If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option.
//
c.PrettyPrint();
// If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion".
// In this case, you must provide a lambda that tells Swagger-Net which actions should be
// included in the docs for a given API version. Like "SingleApiVersion", each call to "Version"
// returns an "Info" builder so you can provide additional metadata per API version.
//
//c.MultipleApiVersions(
// (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
// (vc) =>
// {
// vc.Version("v1", "Swagger_Test");
// vc.Version("v2", "Swagger_Test V2");
// });
// You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API.
// See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details.
// NOTE: These only define the schemes and need to be coupled with a corresponding "security" property
// at the document or operation level to indicate which schemes are required for an operation. To do this,
// you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties
// according to your specific authorization implementation
//
//c.BasicAuth("basic").Description("Basic HTTP Authentication");
//
//c.ApiKey("apiKey", "header", "API Key Authentication");
//
c.OAuth2("oauth2")
.Description("OAuth2 Implicit Grant")
.Flow("accessCode")
.AuthorizationUrl("http://www.facebook.com/dialog/oauth/?client_id=183620338840937&redirect_uri=http%3A%2F%2Fswashbuckletest.azurewebsites.net%2Fswagger")
.TokenUrl("https://graph.facebook.com/oauth/access_token?client_id=183620338840937&redirect_uri=http%3A%2F%2Fswashbuckletest.azurewebsites.net%2Fswagger&client_secret=de81460e907d213dcc4271aa7b1ae88a&grant_type=client_credentials");
// .Scopes(scopes =>
// {
// scopes.Add("read", "Read access to protected resources");
// scopes.Add("write", "Write access to protected resources");
// });
// Set this flag to omit descriptions for any actions decorated with the Obsolete attribute
//c.IgnoreObsoleteActions();
// Comment this setting to disable Access-Control-Allow-Origin
c.AccessControlAllowOrigin("*");
// Each operation be assigned one or more tags which are then used by consumers for various reasons.
// For example, the swagger-ui groups operations according to the first tag of each operation.
// By default, this will be controller name but you can use the "GroupActionsBy" option to
// override with any value.
//
//c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());
// You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate
// the order in which operations are listed. For example, if the default grouping is in place
// (controller name) and you specify a descending alphabetic sort order, then actions from a
// ProductsController will be listed before those from a CustomersController. This is typically
// used to customize the order of groupings in the swagger-ui.
//
//c.OrderActionGroupsBy(new DescendingAlphabeticComparer());
// If you annotate Controllers and API Types with Xml comments:
// http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx
// those comments will be incorporated into the generated docs and UI.
// Just make sure your comment file(s) have extension .XML
// You can add individual files by providing the path to one or
// more Xml comment files.
//
//c.IncludeXmlComments(AppDomain.CurrentDomain.BaseDirectory + "file.ext");
c.IncludeAllXmlComments(ThisAssembly, AppDomain.CurrentDomain.BaseDirectory);
// Swagger-Net makes a best attempt at generating Swagger compliant JSON schemas for the various types
// exposed in your API. However, there may be occasions when more control of the output is needed.
// This is supported through the "MapType" and "SchemaFilter" options:
//
// Use the "MapType" option to override the Schema generation for a specific type.
// It should be noted that the resulting Schema will be placed "inline" for any applicable Operations.
// While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not.
// It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only
// use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a
// complex Schema, use a Schema filter.
//
//c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" });
c.MapType<NodaTime.LocalDate>(() => new Schema { type = "string", format = "date" });
c.MapType<NodaTime.LocalTime>(() => new Schema { type = "string", format = "date-time" });
c.MapType<NodaTime.LocalDateTime>(() => new Schema { type = "string", format = "date-time" });
// If you want to post-modify "complex" Schemas once they've been generated, across the board or for a
// specific type, you can wire up one or more Schema filters.
//
c.SchemaFilter<ApplySchemaVendorExtensions>();
c.SchemaFilter<EnumExampleSchemaFilter>();
//c.SchemaFilter<EnumDefinitionsFilter>();
// In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique
// Schema Id. By default, Swagger-Net does NOT use the full type name in Schema Ids. In most cases, this
// works well because it prevents the "implementation detail" of type namespaces from leaking into your
// Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll
// need to opt out of this behavior to avoid Schema Id conflicts.
//
//c.UseFullTypeNameInSchemaIds();
// Alternatively, you can provide your own custom strategy for inferring SchemaId's for
// describing "complex" types in your API.
//
//c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName);
// Set this flag to omit schema property descriptions for any type properties decorated with the
// Obsolete attribute
//c.IgnoreObsoleteProperties();
// Set this flag to ignore IsSpecified members when serializing and deserializing types.
//
c.IgnoreIsSpecifiedMembers();
// In accordance with the built in JsonSerializer, if disabled Swagger-Net will describe enums as integers.
// You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given
// enum type. Swagger-Net will honor this change out-of-the-box. However, if you use a different
// approach to serialize enums as strings, you can also force Swagger-Net to describe them as strings.
//
c.DescribeAllEnumsAsStrings();
// Similar to Schema filters, Swagger-Net also supports Operation and Document filters:
//
// Post-modify Operation descriptions once they've been generated by wiring up one or more
// Operation filters.
//
//c.OperationFilter<AddDefaultResponse>();
//
// If you've defined an OAuth2 flow as described above, you could use a custom filter
// to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required
// to execute the operation
//
c.OperationFilter<AssignOAuth2SecurityRequirements>();
c.OperationFilter<AddRequiredHeaderParameters>();
c.OperationFilter<ImportFileParamType>();
// Post-modify the entire Swagger document by wiring up one or more Document filters.
// This gives full control to modify the final SwaggerDocument. You should have a good understanding of
// the Swagger 2.0 spec. - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md
// before using this option.
//
//c.DocumentFilter<YamlDocumentFilter>();
c.DocumentFilter<TestDocumentFilter>();
c.DocumentFilter<DocumentFilterAddFakes>();
c.DocumentFilter<RouteTestDocumentFilter>();
c.DocumentFilter<Flip2DateDocumentFilter>();
c.DocumentFilter<SortModelDocumentFilter>();
c.DocumentFilter<StringEnumDocumentFilter>();
c.DocumentFilter<AddExampleDocumentFilter>();
//c.DocumentFilter<ApplyDocumentVendorExtensions>();
c.DocumentFilter<ApplyDocumentFilter_ChangeCompany>();
c.DocumentFilter<AddImageResponseDocumentFilter>();
c.DocumentFilter<HideStuffDocumentFilter>();
//c.DocumentFilter<ParamExampleDocumentFilter>();
c.DocumentFilter(() => new DetailEnumDocumentFilter(new[] {
typeof(ShiftDayOffRule),
typeof(CustomEnum),
typeof(MonthEnum)
}));
//c.DocumentFilter<OptionalPathParamDocumentFilter>();
// In contrast to WebApi, Swagger 2.0 does not include the query string component when mapping a URL
// to an action. As a result, Swagger-Net will raise an exception if it encounters multiple actions
// with the same path (sans query string) and HTTP method. You can workaround this by providing a
// custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs
//
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
// Wrap the default SwaggerGenerator with additional behavior (e.g. caching) or provide an
// alternative implementation for ISwaggerProvider with the CustomProvider option.
//
//c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
};
}
}
public static Action<SwaggerUiConfig> SwaggerUiConf
{
get
{
return c =>
{
// Use the "DocumentTitle" option to change the Document title.
// Very helpful when you have multiple Swagger pages open, to tell them apart.
//
c.DocumentTitle("Swagger_Test");
// Use the "InjectStylesheet" option to enrich the UI with one or more additional CSS stylesheets.
// The file must be included in your project as an "Embedded Resource", and then the resource's
// "Logical Name" is passed to the method as shown below.
//
c.InjectStylesheet(ThisAssembly, "Swagger_Test.Styles.css");
// Use the "InjectJavaScript" option to invoke one or more custom JavaScripts after the swagger-ui
// has loaded. The file must be included in your project as an "Embedded Resource", and then the resource's
// "Logical Name" is passed to the method as shown above.
//
c.InjectJavaScript(ThisAssembly, "Swagger_Test.custom.js");
// The swagger-ui renders boolean data types as a dropdown. By default, it provides "true" and "false"
// strings as the possible choices. You can use this option to change these to something else,
// for example 0 and 1.
//
//c.BooleanValues(new[] { "0", "1" });
// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema.
// The default is false.
//
c.ShowExtensions(true);
// Show pattern, minLength, maxLength, minimum, and maximum fields
//
c.ShowCommonExtensions(true);
// By default, swagger-ui will validate specs against swagger.io's online validator and display the result
// in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the
// feature entirely.
c.SetValidatorUrl("http://online.swagger.io/validator");
//c.DisableValidator();
// Use this option to control how the Operation listing is displayed.
// It can be set to "None" (default), "List" (shows operations for each resource),
// or "Full" (fully expanded: shows operations and their details).
//
c.DocExpansion(DocExpansion.List);
// Controls how models are shown when the API is first rendered. (The user can always switch
// the rendering for a given model by clicking the 'Model' and 'Example Value' links.) It can be
// set to 'model' or 'example', and the default is 'example'.
//
//c.DefaultModelRendering(DefaultModelRender.Model);
// Use this option to control the expansion depth for the model on the model-example section.
//
//c.DefaultModelExpandDepth(0);
// The default expansion depth for models (set to -1 completely hide the models).
//
//c.DefaultModelsExpandDepth(0);
// Limit the number of operations shown to a smaller value
//
c.UImaxDisplayedTags(100);
// Filter the operations works as a search, to disable set to "null"
//
c.UIfilter("''");
// Specify which HTTP operations will have the 'Try it out!' option. An empty parameter list disables
// it for all operations.
//
//c.SupportedSubmitMethods("GET", "HEAD");
// Use the CustomAsset option to provide your own version of assets used in the swagger-ui.
// It's typically used to instruct Swagger-Net to return your version instead of the default
// when a request is made for "index.html". As with all custom content, the file must be included
// in your project as an "Embedded Resource", and then the resource's "Logical Name" is passed to
// the method as shown below.
//
c.CustomAsset("custom", ThisAssembly, "Swagger_Test.custom.html");
// If your API has multiple versions and you've applied the MultipleApiVersions setting
// as described above, you can also enable a select box in the swagger-ui, that displays
// a discovery URL for each version. This provides a convenient way for users to browse documentation
// for different API versions.
//
//c.EnableDiscoveryUrlSelector();
// If your API supports the OAuth2 Implicit flow, and you've described it correctly, according to
// the Swagger 2.0 specification, you can enable UI support as shown below.
//
c.EnableOAuth2Support(
clientId: "183620338840937",
clientSecret: "de81460e907d213dcc4271aa7b1ae88a",
realm: "swaggertestapp",
appName: "swaggertestapp"
//additionalQueryStringParams: new Dictionary<string, string>() { { "foo", "bar" } }
);
};
}
}
public static void Register()
{
GlobalConfiguration.Configuration
.EnableSwagger(SwaggerDocConf)
.EnableSwaggerUi(SwaggerUiConf);
}
private class DocumentFilterAddFakes : IDocumentFilter
{
private PathItem FakePathItem(string tag, int i)
{
var x = new PathItem();
x.get = new Operation()
{
tags = new[] { tag },
operationId = $"{tag}_Get{i}",
consumes = null,
produces = new[] { "application/json" },
parameters = new List<Parameter>()
{
new Parameter()
{
name = "id",
@in = "path",
required = true,
type = "integer",
format = "int32",
@default = 8
}
},
};
x.get.responses = new Dictionary<string, Response>();
x.get.responses.Add("200", new Response() { description = "OK", schema = new Schema() { type = "string" } });
return x;
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
for (int i = 0; i < 10; i++)
swaggerDoc.paths.Add("/Fake/" + i + "/{id}", FakePathItem("Fake", i));
for (int i = 0; i < 2; i++)
swaggerDoc.paths.Add("/Space Sample/" + i + "/{id}", FakePathItem("Space Sample", i));
for (int i = 0; i < 2; i++)
swaggerDoc.paths.Add("/Hash#Sample/" + i + "/{id}", FakePathItem("Hash#Sample", i));
}
}
private class YamlDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
string file = AppDomain.CurrentDomain.BaseDirectory + "swagger_yaml.txt";
if (!File.Exists(file))
{
var serializer = new YamlSerializer();
serializer.SerializeToFile(file, swaggerDoc);
}
}
}
private class OptionalPathParamDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
string actionPath = "/attrib/{payId}";
if (swaggerDoc.paths != null && swaggerDoc.paths.ContainsKey(actionPath))
swaggerDoc.paths[actionPath].get.parameters[0].required = false;
}
}
private class LowerCaseProperties : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
foreach (var def in swaggerDoc.definitions)
{
if (def.Value.properties != null)
{
foreach (var key in def.Value.properties.Keys.ToArray())
{
var value = def.Value.properties[key];
def.Value.properties.Remove(key);
def.Value.properties.Add(key.ToLower(), value);
}
}
}
}
}
private class SortModelDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
string model = "ViewModelTest";
if (swaggerDoc.definitions.ContainsKey(model))
{
var props = swaggerDoc.definitions[model].properties.OrderBy(x => x.Key).ToArray();
swaggerDoc.definitions[model].properties.Clear();
foreach (var prop in props)
{
swaggerDoc.definitions[model].properties.Add(prop.Key, prop.Value);
}
}
}
}
private class TestDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
swaggerDoc.info.description = "Lorem ipsum dolor sit amet, <b>consectetur adipiscing elit</b>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <br>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
"<ul>" +
"<li>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</li>" +
"<li>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.</li>" +
"</ul>" +
"The code for this project is here: https://github.com/heldersepu/Swagger-Net-Test";
/*foreach (var path in swaggerDoc.paths)
{
if (path.Key.Contains("foo") && path.Value.get != null)
{
path.Value.put = path.Value.get;
}
}*/
}
}
private class AddImageResponseDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
if (swaggerDoc.paths != null)
{
if (swaggerDoc.paths.ContainsKey("/api/PngImage"))
{
var post = swaggerDoc.paths["/api/PngImage"].post;
post.produces.Clear();
post.produces.Add("image/png");
}
if (swaggerDoc.paths.ContainsKey("/api/Image"))
{
var put = swaggerDoc.paths["/api/Image"].put;
if (put != null)
{
put.responses["200"].schema = new Schema();
put.responses["200"].schema.type = "file";
}
var patch = swaggerDoc.paths["/api/Image"].patch;
if (patch != null)
{
foreach (var param in patch.parameters)
{
if (param.name == "templateFile")
{
param.type = "file";
param.@in = "formData";
}
}
}
}
}
}
}
private class AddExampleDocumentFilter : IDocumentFilter
{
private List<Guid> Guids
{
get
{
return new List<Guid>
{
Guid.Empty, Guid.Empty
};
}
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
if (swaggerDoc.paths.ContainsKey("/api/Dictionary"))
{
var del = swaggerDoc.paths["/api/Dictionary"].delete;
if (del != null)
{
del.parameters[0].schema.example = Guids;
}
}
}
}
private class RouteTestDocumentFilter : IDocumentFilter
{
const string PATH = "/api/RouteTest/test/{itemid}";
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
if (swaggerDoc.paths != null && swaggerDoc.paths.ContainsKey(PATH))
{
var get = swaggerDoc.paths[PATH].get;
if (get != null)
{
get.parameters.RemoveAt(0);
get.parameters[0].@in = "path";
get.parameters[0].required = true;
foreach (var param in get.parameters)
{
int pos = param.name.IndexOf('.');
if (pos > 0)
param.name = param.name.Substring(pos + 1);
}
}
}
}
}
private class DetailEnumDocumentFilter : IDocumentFilter
{
private Type[] enums;
public DetailEnumDocumentFilter(Type[] enums)
{
this.enums = enums;
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
var schema = new Schema { properties = new Dictionary<string, Schema>() };
foreach (Type e in enums)
{
var example = new Dictionary<string, int>();
foreach (var item in Enum.GetValues(e))
{
example.Add(item.ToString(), (int)item);
}
schema.properties.Add(e.Name, new Schema { example = example });
}
swaggerDoc.definitions.Add("Enums_Definitions", schema);
}
}
private class StringEnumDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
if (swaggerDoc.paths != null)
{
foreach (var path in swaggerDoc.paths)
{
ProcessOperation(path.Value.get);
ProcessOperation(path.Value.put);
ProcessOperation(path.Value.post);
ProcessOperation(path.Value.delete);
ProcessOperation(path.Value.options);
ProcessOperation(path.Value.head);
ProcessOperation(path.Value.patch);
}
}
}
private void ProcessOperation(Operation op)
{
if (op != null)
{
foreach (var param in op.parameters)
{
if (param.pattern != null && param.pattern.Contains("|"))
{
param.@enum = param.pattern
.Replace("^", "")
.Replace("(", "")
.Replace(")", "")
.Split('|');
}
}
}
}
}
public class AddRequiredHeaderParameters : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
if (operation.operationId == "ValueProvider_Put")
{
operation.parameters.Add(HeaderParam("CID", "101"));
operation.parameters.Add(HeaderParam("QID", "102"));
operation.parameters.Add(HeaderParam("QID-V2", "1022"));
operation.parameters.Add(HeaderParam("Proxy-Authorization", "103"));
operation.parameters.Add(HeaderParam("Proxy_Authorization", "1031"));
operation.parameters.Add(HeaderParam("Proxy-CustomAuth", "1032"));
operation.parameters.Add(HeaderParam("Proxy-Test", "1033"));
}
else if (operation.tags[0].Equals("FooBar"))
{
// add other header parameters here
}
}
public Parameter HeaderParam(string name, string defaultValue, bool required = true, string type = "string", string description = "")
{
return new Parameter
{
name = name,
@in = "header",
@default = defaultValue,
type = type,
description = description,
required = required
};
}
}
private class ApplyDocumentFilter_ChangeCompany : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
schemaRegistry.GetOrRegister(typeof(GeolocateResponse));
schemaRegistry.GetOrRegister(typeof(CountingLock1));
schemaRegistry.GetOrRegister(typeof(CountingLock2));
schemaRegistry.GetOrRegister(typeof(Coordinate));
if (swaggerDoc.definitions != null && swaggerDoc.definitions.ContainsKey("Company"))
{
swaggerDoc.definitions.Add("Company123", swaggerDoc.definitions["Company"]);
}
if (swaggerDoc.paths != null && swaggerDoc.paths.ContainsKey("/api/Company"))
{
var get = swaggerDoc.paths["/api/Company"].get;
var response = get.responses["200"];
response.schema.@ref = "#/definitions/Company123";
response.description =
"## MARKDOWN DoubleHash \n" +
"### MARKDOWN TripleHash \n" +
"`MARKDOWN code` \n " + get.description;
var post = swaggerDoc.paths["/api/Company"].post;
post.description = "foo | bar\r\n----|----\r\nbaz | qux | quux";
}
}
}
public static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
{
return (apiDesc.Route.RouteTemplate.ToLower().Contains(targetApiVersion.ToLower()));
}
private class Flip2DateDocumentFilter : IDocumentFilter
{
private List<string> DateTypes(Type AttribType)
{
var list = new List<string>();
foreach (var p in AttribType.GetProperties())
if (p.CustomAttributes?.Count() > 0)
list.Add(p.Name);
return list;
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
var t = typeof(Controllers.Dates);
if (swaggerDoc.definitions[t.Name] != null)
{
var dates = DateTypes(t);
if (dates.Count() > 0)
foreach (var p in swaggerDoc.definitions[t.Name].properties)
if (dates.Contains(p.Key) && p.Value.format == "date-time")
p.Value.format = "date";
}
}
}
private class ApplyDocumentVendorExtensions : IDocumentFilter
{
const string CONTRACT_TYPES = "ContractTypes";
private List<Type> ContractTypes(Type AttribType)
{
var types = new List<Type>();
var memCache = MemoryCache.Default.Get(CONTRACT_TYPES);
if (memCache == null)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type type in assembly.GetTypes())
{
var attribs = type.GetCustomAttributes(AttribType, false);
if (attribs != null && attribs.Length > 0)
{
types.Add(type);
}
}
}
var policy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromHours(24) };
MemoryCache.Default.Add(CONTRACT_TYPES, types, policy);
}
else
{
types = (List<Type>)memCache;
}
return types;
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
schemaRegistry.GetOrRegister(typeof(ExtraType));
//schemaRegistry.GetOrRegister(typeof(BigClass));
foreach (var type in ContractTypes(typeof(ContractClassAttribute)))
{
schemaRegistry.GetOrRegister(type);
}
var paths = new Dictionary<string, PathItem>(swaggerDoc.paths);
swaggerDoc.paths.Clear();
foreach (var path in paths)
{
if (path.Key.Contains("foo"))
swaggerDoc.paths.Add(path);
}
}
}
private class HideStuffDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
foreach (var definition in swaggerDoc.definitions)
{
foreach (var prop in definition.Value.properties.ToList())
{
if (prop.Value.maxLength == 9999)
definition.Value.properties.Remove(prop);
}
}
}
}
private class ParamExampleDocumentFilter : IDocumentFilter
{
const string PATH = "/api/IHttpActionResult/{id}";
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
if (swaggerDoc.paths.ContainsKey(PATH))
{
foreach (var param in swaggerDoc.paths[PATH].get.parameters)
{
if (param.example != null && param.schema == null)
param.schema = new Schema { example = param.example };
}
}
}
}
public class EnumDefinitionsFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaRegistry schemaRegistry, Type type)
{
if (model.properties != null)
{
var enumProperties = model.properties.Where(p => p.Value.@enum != null)
.Union(model.properties.Where(p => p.Value.items?.@enum != null)).ToList();
if (enumProperties.Count > 0)
{
var enums = type.GetProperties()
.Select(p => Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType.GetElementType() ??
p.PropertyType.GetGenericArguments().FirstOrDefault() ?? p.PropertyType)
.Where(p => p.IsEnum)
.Distinct()
.ToList();
foreach (var enumProperty in enumProperties)
{
var enumPropertyValue = enumProperty.Value.@enum != null ? enumProperty.Value : enumProperty.Value.items;
var enumValues = enumPropertyValue.@enum.Select(e => $"{e}").ToList();
var enumType = enums.FirstOrDefault(p =>
{
var enumNames = Enum.GetNames(p);
if (enumNames.Except(enumValues, StringComparer.InvariantCultureIgnoreCase).Any())
return false;
if (enumValues.Except(enumNames, StringComparer.InvariantCultureIgnoreCase).Any())
return false;
return true;
});
if (enumType != null)
{
if (schemaRegistry.Definitions.ContainsKey(enumType.Name) == false)
schemaRegistry.Definitions.Add(enumType.Name, enumPropertyValue);
var schema = new Schema
{
@ref = $"#/definitions/{enumType.Name}"
};
if (enumProperty.Value.@enum != null)
{
model.properties[enumProperty.Key] = schema;
}
else if (enumProperty.Value.items?.@enum != null)
{
enumProperty.Value.items = schema;
}
}
}
}
}
}
}
private class ApplySchemaVendorExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
// Modify the example values in the final SwaggerDocument
//
if (schema.properties != null)
{
foreach (var p in schema.properties)
{
Debug.WriteLine($"{p.Key} {p.Value.format}");
if (p.Value.example == null)
{
switch (p.Value.format)
{
case "uuid":
p.Value.example = Guid.Empty;
break;
case "int32":
p.Value.example = 123;
break;
case "double":
p.Value.example = 9858.216;
break;
case "date":
case "date-time":
p.Value.example = DateTime.Now.ToString("yyyy-MM-dd");
break;
default:
switch (p.Key)
{
case "index":
p.Value.example = new Dictionary<int, string>
{ { 1, "abc" }, { 2, "def" } };
break;
case "keys":
p.Value.example = new List<string>
{ "abc", "def", "ghi" };
break;
case "xyz":
p.Value.example = "abcasdfasd";
p.Value.maxLength = 10;
break;
case "Points":
p.Value.example = new List<Location>
{
new Location{Lat=1, Lon=2},
new Location{Lat=5, Lon=6},
};
break;
}
break;
}
}
}
}
}
}
private class EnumExampleSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (type == typeof(ShiftDayOffRule))
{
var example = new Dictionary<string, int>();
foreach (var item in Enum.GetValues(typeof(ShiftDayOffRule)))
{
example.Add(item.ToString(), (int)item);
}
schema.example = example;
}
}
}
public class AssignOAuth2SecurityRequirements : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();
if (actFilters.Select(f => f.Instance).OfType<AllowAnonymousAttribute>().Any())
return; // must be an anonymous method
if (operation.security == null)
operation.security = new List<IDictionary<string, IEnumerable<string>>>();
var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
{
{"oauth2", new List<string> {}}
};
operation.security.Add(oAuthRequirements);
}
}
public class ImportFileParamType : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var requestAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerFormAttribute>();
foreach (var attr in requestAttributes)
{
operation.parameters = operation.parameters ?? new List<Parameter>();
operation.parameters.Add(
new Parameter
{
description = attr.Description,
name = attr.Name,
@in = "formData",
required = true,
type = "file",
}
);
operation.consumes.Add("multipart/form-data");
}
}
}
}
}