├── .gitattributes ├── .gitignore ├── LICENSE ├── assets ├── Enrichment.png ├── MultiFormat.png ├── enablexmldocumentation.png ├── logo.ico ├── logo.png └── logo_notext.png ├── docs ├── settings.md └── sources.md ├── readme.md └── src └── ServiceStack.IntroSpec ├── DemoService ├── App.config ├── DemoService.cs ├── DemoService.csproj ├── Program.cs └── TypeSpecs │ └── FallbackRequestSpec.cs ├── IntroSpec └── IntroSpec │ ├── DemoService.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── IntroSpec.csproj │ ├── Properties │ ├── AssemblyInfo.cs │ └── PublishProfiles │ │ └── WebDeploy.pubxml │ ├── TypeSpecs │ └── FallbackRequestSpec.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ └── packages.config ├── ServiceStack.IntroSpec.Tests ├── ApiDocumentationGeneratorTests.cs ├── ApiDocumentationProviderTests.cs ├── App.config ├── DTO │ └── SpecRequestTests.cs ├── Enrichers │ ├── AbstractClassEnricherTests.cs │ ├── FallbackEnricherTests.cs │ ├── Infrastructure │ │ ├── ActionEnricherManagerTests.cs │ │ ├── PropertyEnricherManagerTests.cs │ │ ├── RequestEnricherManagerTests.cs │ │ └── ResourceEnricherManagerTests.cs │ ├── ReflectionEnricherTests.cs │ └── XmlEnricherTests.cs ├── Extensions │ ├── AccessExtensionsTests.cs │ ├── CollectionExtensionsTests.cs │ ├── DictionaryExtensionsTests.cs │ ├── GetValueExtensionsTests.cs │ ├── OperationExtensionsTests.cs │ ├── ReflectionExtensionsTests.cs │ ├── StringExtensionsTests.cs │ └── UrlExtensionsTests.cs ├── Fixtures │ ├── AppHostCollection.cs │ └── AppHostFixture.cs ├── IntroSpecFeatureTests.cs ├── Models │ ├── PermissionsTests.cs │ ├── PropertyConstraintTests.cs │ ├── RelativePathTests.cs │ └── StatusCodesTests.cs ├── Postman │ └── PostmanCollectionGeneratorTests.cs ├── ResultTests │ └── ResultTests.cs ├── ServiceStack.IntroSpec.Tests.csproj ├── Services │ └── ApiDocumentationFilterTests.cs ├── Settings │ ├── ApiSpecConfigTests.cs │ └── DocumenterSettingsTests.cs ├── TypeSpec │ ├── AbstractRequestSpecTests.cs │ ├── ApiDtoSpecTests.cs │ ├── DocumentationClassLocatorTests.cs │ └── PropertyMetadataTests.cs ├── Utilities │ ├── ApiSpecMetadataUtilitiesTests.cs │ ├── EnumUtilitiesTests.cs │ └── MimeTypeUtilitiesTests.cs ├── Validators │ └── ApiSpecSettingsValidatorTest.cs └── XmlDocumentation │ ├── MemberInfoExtensionsTests.cs │ └── XmlDocumentationLookupTests.cs ├── ServiceStack.IntroSpec.sln └── ServiceStack.IntroSpec ├── ApiDocumentationGenerator.cs ├── ApiSpecFeature.cs ├── ConfigKeys.cs ├── Constants.cs ├── DTO ├── DtoGrouping.cs ├── IFilterableSpecRequest.cs ├── SpecMetadataRequest.cs ├── SpecMetadataResponse.cs ├── SpecRequest.cs └── SpecResponse.cs ├── Enrichers ├── AbstractClassEnricher.cs ├── FallbackEnricher.cs ├── Infrastructure │ ├── ActionEnricherManager.cs │ ├── EnricherCoordinator.cs │ ├── IActionEnricherManager.cs │ ├── PropertyEnricherManager.cs │ ├── RequestEnricherManager.cs │ ├── ResourceEnricherManager.cs │ └── ResourceModel.cs ├── Interfaces │ ├── IActionEnricher.cs │ ├── IApiResourceEnricher.cs │ ├── IEnrich.cs │ ├── IPropertyEnricher.cs │ ├── IRequestEnricher.cs │ ├── IResourceEnricher.cs │ └── ISecurityEnricher.cs ├── ReflectionEnricher.cs └── XmlEnricher.cs ├── Extensions ├── AccessExtensions.cs ├── ApiDocumentationExtensions.cs ├── CollectionExtensions.cs ├── DictionaryExtensions.cs ├── GetValueExtensions.cs ├── OperationExtensions.cs ├── ReflectionExtensions.cs ├── StringExtensions.cs └── UrlExtensions.cs ├── HttpVerbs.cs ├── IApiDocumentationGenerator.cs ├── IApiSpecSettings.cs ├── Infrastructure └── Result.cs ├── IntroSpecFeature.cs ├── Linq └── PredicateBuilder.cs ├── Models ├── ApiAction.cs ├── ApiClrType.cs ├── ApiContact.cs ├── ApiDocumentation.cs ├── ApiPlugin.cs ├── ApiPropertyDocumentation.cs ├── ApiResourceDocumentation.cs ├── ApiResourceType.cs ├── ApiSecurity.cs ├── IApiAction.cs ├── IApiMetadata.cs ├── IApiRequest.cs ├── IApiResourceType.cs ├── IApiSpec.cs ├── ISecured.cs ├── Permissions.cs ├── PropertyConstraint.cs ├── RelativePath.cs └── StatusCode.cs ├── Postman ├── DTO │ ├── PostmanRequest.cs │ └── PostmanResponse.cs ├── Models │ ├── Postman.cs │ ├── PostmanFolder.cs │ ├── PostmanSpecCollection.cs │ ├── PostmanSpecData.cs │ └── PostmanSpecRequest.cs └── Services │ ├── ApiSpecPostmanService.cs │ └── PostmanCollectionGenerator.cs ├── ServiceStack.IntroSpec.csproj ├── ServiceStack.IntroSpec.nuspec ├── Services ├── ApiDocumentationProvider.cs ├── ApiSpecMetadataService.cs ├── ApiSpecService.cs └── IApiDocumentationProvider.cs ├── Settings ├── ApiSpecConfig.cs ├── DocumenterSettings.cs ├── DocumenterSettingsScope.cs └── EnrichmentStrategy.cs ├── TypeSpec ├── AbstractRequestSpec.cs ├── AbstractTypeSpec.cs ├── DocumentationClassLocator.cs ├── IApiResource.cs ├── IFluentInterface.cs ├── IProperty.cs ├── IPropertyMetadata.cs └── PropertyMetadata.cs ├── Utilities ├── ApiSpecMetadataUtilities.cs ├── EnumUtilities.cs └── MimeTypeUtilities.cs ├── Validators ├── ApiContactValidator.cs ├── ApiSpecConfigValidator.cs └── ApiSpecSettingsValidator.cs └── XmlDocumentation ├── IXmlDocumentationLookup.cs ├── IXmlDocumentationReader.cs ├── MemberInfoExtensions.cs ├── XmlDocumentation.cs ├── XmlDocumentationLookup.cs └── XmlDocumentationReader.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | *.DotSettings 214 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/LICENSE -------------------------------------------------------------------------------- /assets/Enrichment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/Enrichment.png -------------------------------------------------------------------------------- /assets/MultiFormat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/MultiFormat.png -------------------------------------------------------------------------------- /assets/enablexmldocumentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/enablexmldocumentation.png -------------------------------------------------------------------------------- /assets/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/logo.ico -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/logo.png -------------------------------------------------------------------------------- /assets/logo_notext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwlicious/servicestack-introspec/d006a67211ab4dbcbb69622c313646f7e87a6cf2/assets/logo_notext.png -------------------------------------------------------------------------------- /docs/settings.md: -------------------------------------------------------------------------------- 1 | ## Documenter Settings 2 | `DocumenterSettings` is a static class that provides the ability to set default/fallback values and configure how some of the plugin works. 3 | 4 | All of the values that are required by the plugin have defaults as outlined below. These can be changed as required. 5 | 6 | The `DocumenterSettings` class is static so properties that are updated will be set globally. 7 | 8 | ```csharp 9 | DocumenterSettings.FallbackCategory = "Fallback Category"; 10 | DocumenterSettings.DefaultTags = new[] { "DefaultTag" }; 11 | ``` 12 | 13 | For ease of setting multiple values there is a `With()` method. 14 | ```csharp 15 | DocumenterSettings.With(fallbackCategory: "Fallback Category", defaultTags: new[] { "DefaultTag" }); 16 | ``` 17 | 18 | The `DocumenterSettings` class can also be scoped to certain blocks of code using the 19 | `With()` method. When this is disposed any previously set values will be returned. 20 | ```csharp 21 | // Set fallback category globally 22 | DocumenterSettings.FallbackCategory = "Global Category"; 23 | 24 | using (DocumenterSettings.With(fallbackCategory: "Scoped Category")) 25 | { 26 | Console.WriteLine(DocumenterSettings.FallbackCategory); // output - "Scoped Category" 27 | } 28 | Console.WriteLine(DocumenterSettings.FallbackCategory); // output - "Global Category" 29 | 30 | ``` 31 | 32 | ### Settings 33 | The following is a list of the available properties, their use and any defaults. 34 | 35 | | Name | Type | Use | Default | 36 | | --- | --- | --- | --- | 37 | | ReplacementVerbs | `IEnumerable` | List of verbs that are output in documetation if `Any` supported by service. | GET, POST | 38 | | Assemblies | `IEnumerable` | List of assemblies to scan for implementations of `AbstractTypeSpec<>` | `Assembly.GetEntryAssembly()` | 39 | | CollectionStrategy | `EnrichmentStrategy` | How to deal with collection properties. Options are `Union` - each enricher will provide values and result will be unique union of these. `SetIfEmpty` - lower level enrichers will only provide values if the collection is null or empty. | `EnrichmentStrategy.Union` | 40 | | FallbackNotes | `string` | Notes to fallback to if no other provided for resource | | 41 | | FallbackCategory | `string` | Category to fallback to if no other provided | | 42 | | FallbackRouteNotes | `string` | Notes to fallback to if no other provided for route | | 43 | | DefaultStatusCodes| `IEnumerable` | Status code to be set for all requests (e.g. 429 if RateLimiting is enabled). The usage of these will depend on value of CollectionStrategy. | | 44 | | DefaultTags| `IEnumerable` | Default tags to set for requests. The usage of these will depend on value of CollectionStrategy. | | 45 | | DefaultContentTypes| `IEnumerable` | Default content types (e.g. application/x-custom) to set for requests. The usage of these will depend on value of CollectionStrategy. | | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/DemoService/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/DemoService/DemoService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ..\packages\ServiceStack.Api.Swagger.5.0.0\lib\net45\ServiceStack.Api.Swagger.dll 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/DemoService/Program.cs: -------------------------------------------------------------------------------- 1 | namespace DemoService 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Net; 7 | using Funq; 8 | using ServiceStack; 9 | using ServiceStack.Api.Swagger; 10 | using ServiceStack.IntroSpec; 11 | using ServiceStack.IntroSpec.Models; 12 | using ServiceStack.IntroSpec.Settings; 13 | using ServiceStack.Logging; 14 | using ServiceStack.Text; 15 | using ServiceStack.Validation; 16 | 17 | public class Program 18 | { 19 | private static readonly ILog log = LogManager.GetLogger(typeof(Program)); 20 | 21 | static void Main(string[] args) 22 | { 23 | var urlBase = "http://*:8090/"; 24 | var x = new AppHost().Init().Start(urlBase); 25 | $"ServiceStack SelfHost listening at {urlBase} ".Print(); 26 | Process.Start("http://localhost:8090/"); 27 | 28 | if (x.StartUpErrors.Count > 0) 29 | { 30 | foreach (var responseStatus in x.StartUpErrors) 31 | log.Warn($"Error in Startup. {responseStatus.Message} - {responseStatus.StackTrace}"); 32 | } 33 | 34 | Console.ReadLine(); 35 | } 36 | } 37 | 38 | public class AppHost : AppSelfHostBase 39 | { 40 | public AppHost() : base("DemoDocumentationService", typeof(DemoService).Assembly) 41 | { 42 | } 43 | 44 | public override void Configure(Container container) 45 | { 46 | SetConfig(new HostConfig 47 | { 48 | ApiVersion = "2.0" 49 | }); 50 | 51 | LogManager.LogFactory = new ConsoleLogFactory(); 52 | 53 | SetupPlugins(); 54 | 55 | container.RegisterValidators(typeof(MyDtoValidator).Assembly); 56 | } 57 | 58 | private void SetupPlugins() 59 | { 60 | Plugins.Add(new PostmanFeature()); 61 | Plugins.Add(new SwaggerFeature()); 62 | 63 | //DocumenterSettings.ReplacementVerbs = new[] { "GET", "PUT", "POST", "DELETE" }; 64 | //DocumenterSettings.CollectionStrategy = EnrichmentStrategy.SetIfEmpty; 65 | 66 | JsConfig.IncludePublicFields = true; // Serialize Fields 67 | DocumenterSettings.DefaultStatusCodes = new List 68 | { 69 | new StatusCode { Code = 429, Description = "This is rate limited", Name = "Too Many Requests" }, 70 | ((StatusCode)HttpStatusCode.Forbidden).WithDescription("Set at a global level"), 71 | ((StatusCode)200).WithDescription("Ok. Set at a global level") 72 | }; 73 | DocumenterSettings.FallbackCategory = "Fallback Category"; 74 | DocumenterSettings.DefaultTags = new[] { "DefaultTag" }; 75 | DocumenterSettings.FallbackNotes = "Default notes, set at a global level"; 76 | 77 | // Read settings from appsettings, see readme 78 | Plugins.Add(new IntroSpecFeature()); 79 | 80 | // or directly setting properties builder 81 | /*Plugins.Add(new IntroSpecFeature 82 | { 83 | Description = "This is a demo app host setup for testing documentation.", 84 | LicenseUrl = new Uri("http://mozilla.org/MPL/2.0/") 85 | });*/ 86 | 87 | // or using the fluent builder (deprecated) 88 | /*Plugins.Add(new ApiSpecFeature(config => 89 | config.WithDescription("This is a demo app host setup for testing documentation.") 90 | .WithLicenseUrl(new Uri("http://mozilla.org/MPL/2.0/")) 91 | .WithContactName("Joe Bloggs") 92 | .WithContactEmail("email@address.com")));*/ 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/DemoService/TypeSpecs/FallbackRequestSpec.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace DemoService.TypeSpecs 3 | { 4 | using ServiceStack.Html; 5 | using ServiceStack.IntroSpec; 6 | using ServiceStack.IntroSpec.Models; 7 | using ServiceStack.IntroSpec.TypeSpec; 8 | 9 | public class FallbackRequestSpec : AbstractRequestSpec 10 | { 11 | public FallbackRequestSpec() 12 | { 13 | Title = "Fallback request title from abstract class"; 14 | Description = "Fallback request desc from abstract"; 15 | Notes = "Fallback request notes from abstract"; 16 | 17 | Category = "Category1"; 18 | 19 | AddTags("Tag1", "Tag2", "Tag3"); 20 | 21 | AddStatusCodes(HttpVerbs.Get, 22 | new StatusCode 23 | { 24 | Code = 503, 25 | Name = "Service Unavailable", 26 | Description = "Service is unavailable, try again later" 27 | }, 28 | new StatusCode 29 | { 30 | Code = 204, 31 | Name = "No Content", 32 | Description = "A more verbose explanation that won't be shown" 33 | }); 34 | 35 | AddStatusCodes(HttpVerbs.Post, (StatusCode) 400); 36 | 37 | AddContentTypes("application/hal+json"); 38 | 39 | AddRouteNotes(HttpVerbs.Get, "This is a note about GET route"); 40 | 41 | For(t => t.MyField) 42 | .With(p => p.Title, "A field, not a property"); 43 | 44 | For(t => t.Name) 45 | .With(p => p.Title, "Name parameter abstract class definition") 46 | .With(p => p.IsRequired, true) 47 | .With(p => p.Description, "Description from abstract class"); 48 | 49 | For(t => t.Age) 50 | .With(p => p.Title, "Age is optional") 51 | .With(p => p.IsRequired, false) 52 | .With(p => p.Constraint, PropertyConstraint.RangeConstraint("Age Range", 0, 120)); 53 | 54 | For(t => t.MyName) 55 | .With(p => p.Description, "This is a complex return type"); 56 | } 57 | } 58 | 59 | public class NameDocumenter : AbstractTypeSpec 60 | { 61 | public NameDocumenter() 62 | { 63 | Title = "Title of Name"; 64 | Description = "This comes from AbstractTypeSpec implementation and will be used everywhere that Name is."; 65 | 66 | For(t => t.Forename) 67 | .With(p => p.IsRequired, true); 68 | } 69 | } 70 | 71 | public class SecureDocumenter : AbstractRequestSpec 72 | { 73 | public SecureDocumenter() 74 | { 75 | AddContentTypes(HttpVerbs.Delete, "application/x-custom"); 76 | 77 | AddStatusCodes(HttpVerbs.Delete, ((StatusCode)400).WithDescription("Only for delete")); 78 | } 79 | } 80 | 81 | public class ComplexResponseDocumenter : AbstractTypeSpec 82 | { 83 | public ComplexResponseDocumenter() 84 | { 85 | Title = "Complex Response Title"; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="IntroSpec.Global" Language="C#" %> 2 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using Funq; 5 | using ServiceStack; 6 | using ServiceStack.Api.Swagger; 7 | using ServiceStack.IntroSpec; 8 | using ServiceStack.IntroSpec.Models; 9 | using ServiceStack.IntroSpec.Settings; 10 | using ServiceStack.Logging; 11 | using ServiceStack.Text; 12 | 13 | namespace IntroSpec 14 | { 15 | public class AppHost : AppHostBase 16 | { 17 | public AppHost() : base("DemoDocumentationService", typeof(DemoService.DemoService).Assembly) 18 | { 19 | } 20 | 21 | public override void Configure(Container container) 22 | { 23 | SetConfig(new HostConfig 24 | { 25 | ApiVersion = "2.0", 26 | HandlerFactoryPath = "something" 27 | }); 28 | 29 | LogManager.LogFactory = new ConsoleLogFactory(); 30 | 31 | SetupPlugins(); 32 | } 33 | 34 | private void SetupPlugins() 35 | { 36 | Plugins.Add(new MetadataFeature()); 37 | Plugins.Add(new PostmanFeature()); 38 | Plugins.Add(new SwaggerFeature()); 39 | 40 | JsConfig.IncludePublicFields = true; // Serialize Fields 41 | DocumenterSettings.DefaultStatusCodes = new List 42 | { 43 | new StatusCode { Code = 429, Description = "This is rate limited", Name = "Too Many Requests" }, 44 | ((StatusCode)HttpStatusCode.Forbidden).WithDescription("Set at a global level"), 45 | ((StatusCode)200).WithDescription("Ok. Set at a global level") 46 | }; 47 | DocumenterSettings.FallbackCategory = "Fallback Category"; 48 | DocumenterSettings.DefaultTags = new[] { "DefaultTag" }; 49 | DocumenterSettings.FallbackNotes = "Default notes, set at a global level"; 50 | 51 | Plugins.Add(new IntroSpecFeature 52 | { 53 | Description = "This is a demo app host setup for testing documentation.", 54 | ContactName = "Joe Bloggs", 55 | ContactEmail = "email@address.com", 56 | LicenseUrl = new Uri("http://mozilla.org/MPL/2.0/") 57 | }); 58 | } 59 | } 60 | 61 | public class Global : System.Web.HttpApplication 62 | { 63 | protected void Application_Start(object sender, EventArgs e) 64 | { 65 | new AppHost().Init(); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("IntroSpec")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("IntroSpec")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d32eb8ee-3954-453b-a3fd-07c605c86160")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Properties/PublishProfiles/WebDeploy.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | MSDeploy 9 | Release 10 | Any CPU 11 | http://introspec.servicestack.net 12 | True 13 | False 14 | awstest.servicestack.net 15 | IntroSpec 16 | 17 | True 18 | WMSVC 19 | True 20 | deploy 21 | <_SavePWD>True 22 | 23 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/TypeSpecs/FallbackRequestSpec.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace DemoService.TypeSpecs 3 | { 4 | using ServiceStack.IntroSpec; 5 | using ServiceStack.IntroSpec.Models; 6 | using ServiceStack.IntroSpec.TypeSpec; 7 | 8 | public class FallbackRequestSpec : AbstractRequestSpec 9 | { 10 | public FallbackRequestSpec() 11 | { 12 | Title = "Fallback request title from abstract class"; 13 | Description = "Fallback request desc from abstract"; 14 | Notes = "Fallback request notes from abstract"; 15 | 16 | Category = "Category1"; 17 | 18 | AddTags("Tag1", "Tag2", "Tag3"); 19 | 20 | AddStatusCodes(HttpVerbs.Get, 21 | new StatusCode 22 | { 23 | Code = 503, 24 | Name = "Service Unavailable", 25 | Description = "Service is unavailable, try again later" 26 | }, 27 | new StatusCode 28 | { 29 | Code = 204, 30 | Name = "No Content", 31 | Description = "A more verbose explanation that won't be shown" 32 | }); 33 | 34 | AddStatusCodes(HttpVerbs.Post, (StatusCode) 400); 35 | 36 | AddContentTypes("application/hal+json"); 37 | 38 | AddRouteNotes(HttpVerbs.Get, "This is a note about GET route"); 39 | 40 | For(t => t.MyField) 41 | .With(p => p.Title, "A field, not a property"); 42 | 43 | For(t => t.Name) 44 | .With(p => p.Title, "Name parameter abstract class definition") 45 | .With(p => p.IsRequired, true) 46 | .With(p => p.Description, "Description from abstract class"); 47 | 48 | For(t => t.Age) 49 | .With(p => p.Title, "Age is optional") 50 | .With(p => p.IsRequired, false) 51 | .With(p => p.Constraint, PropertyConstraint.RangeConstraint("Age Range", 0, 120)); 52 | 53 | For(t => t.MyName) 54 | .With(p => p.Description, "This is a complex return type"); 55 | } 56 | } 57 | 58 | public class NameDocumenter : AbstractTypeSpec 59 | { 60 | public NameDocumenter() 61 | { 62 | Title = "Title of Name"; 63 | Description = "This comes from AbstractTypeSpec implementation and will be used everywhere that Name is."; 64 | 65 | For(t => t.Forename) 66 | .With(p => p.IsRequired, true); 67 | } 68 | } 69 | 70 | public class SecureDocumenter : AbstractRequestSpec 71 | { 72 | public SecureDocumenter() 73 | { 74 | AddContentTypes(HttpVerbs.Delete, "application/x-custom"); 75 | 76 | AddStatusCodes(HttpVerbs.Delete, ((StatusCode)400).WithDescription("Only for delete")); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 | 9 | 11 |
14 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 59 | 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/IntroSpec/IntroSpec/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/ApiDocumentationProviderTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests 6 | { 7 | using System.Collections.Generic; 8 | using FakeItEasy; 9 | using Fixtures; 10 | using FluentAssertions; 11 | using Host; 12 | using IntroSpec; 13 | using IntroSpec.Models; 14 | using IntroSpec.Services; 15 | using Xunit; 16 | 17 | [Collection("AppHost")] 18 | public class ApiDocumentationProviderTests 19 | { 20 | private readonly AppHostFixture fixture; 21 | 22 | public ApiDocumentationProviderTests(AppHostFixture fixture) 23 | { 24 | this.fixture = fixture; 25 | } 26 | 27 | [Fact] 28 | public void GetApiDocumentation_ReturnsDocumentationFromApiSpecFeature() 29 | { 30 | var generator = A.Fake(); 31 | var apiDocumentation = new ApiDocumentation(); 32 | 33 | var apiSpecFeature = new IntroSpecFeature().WithGenerator(generator); 34 | A.CallTo(() => 35 | generator.GenerateDocumentation(A>.Ignored, fixture.AppHost, apiSpecFeature)) 36 | .Returns(apiDocumentation); 37 | 38 | fixture.AppHost.LoadPlugin(apiSpecFeature); 39 | 40 | var provider = new ApiDocumentationProvider(); 41 | provider.GetApiDocumentation("http://anything/").Should().Be(apiDocumentation); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/DTO/SpecRequestTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.DTO 6 | { 7 | using DataAnnotations; 8 | using FluentAssertions; 9 | using IntroSpec.DTO; 10 | using Xunit; 11 | 12 | public class SpecRequestTests 13 | { 14 | [Fact] 15 | public void HasExcludeAttribute() 16 | => 17 | typeof(SpecRequest).FirstAttribute().Feature.Should().Be(Feature.Metadata | 18 | Feature.ServiceDiscovery); 19 | 20 | [Fact] 21 | public void HasRouteAttribute() 22 | => typeof (SpecRequest).FirstAttribute().Path.Should().Be("/spec"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Enrichers/FallbackEnricherTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Enrichers 6 | { 7 | using System; 8 | using FluentAssertions; 9 | using Host; 10 | using IntroSpec.Enrichers; 11 | using IntroSpec.Models; 12 | using IntroSpec.Settings; 13 | using Xunit; 14 | 15 | public class FallbackEnricherTests 16 | { 17 | private readonly FallbackEnricher fallback = new FallbackEnricher(); 18 | 19 | private Operation operation => new Operation(); 20 | private Type type => typeof(AllAttributes); 21 | 22 | [Fact] 23 | public void GetTitle_ReturnsNull() => fallback.GetTitle(type).Should().BeNull(); 24 | 25 | [Fact] 26 | public void GetDescription_ReturnsNull() => fallback.GetDescription(type).Should().BeNull(); 27 | 28 | [Fact] 29 | public void GetRelativePath_ReturnsNull() => fallback.GetRelativePaths(operation, "GET").Should().BeNull(); 30 | 31 | [Fact] 32 | public void GetNotes_ReturnsFallbackNotes_FromSettings() 33 | { 34 | const string notes = "this is a good api"; 35 | using (DocumenterSettings.With(fallbackNotes: notes)) 36 | fallback.GetNotes(type).Should().Be(notes); 37 | } 38 | 39 | [Fact] 40 | public void GetStatusCodes_ReturnsStatusCodes_FromSettings() 41 | { 42 | var codes = new[] { (StatusCode)200, (StatusCode)409 }; 43 | using (DocumenterSettings.With(defaultStatusCodes: codes)) 44 | fallback.GetStatusCodes(operation, "GET").Should().BeEquivalentTo(codes); 45 | } 46 | 47 | [Fact] 48 | public void GetCategory_ReturnsFallbackNotes_FromSettings() 49 | { 50 | const string category = "danger"; 51 | using (DocumenterSettings.With(fallbackCategory: category)) 52 | fallback.GetCategory(operation).Should().Be(category); 53 | } 54 | 55 | [Fact] 56 | public void GetTags_ReturnTags_FromSettings() 57 | { 58 | var tags = new[] { "Tag1", "Tag2" }; 59 | using (DocumenterSettings.With(defaultTags: tags)) 60 | fallback.GetTags(operation).Should().BeEquivalentTo(tags); 61 | } 62 | 63 | [Fact] 64 | public void GetTags_ReturnsContentTypes_FromSettings() 65 | { 66 | var contentTypes = new[] { "test/jsv", "text/csv" }; 67 | using (DocumenterSettings.With(defaultContentTypes: contentTypes)) 68 | fallback.GetContentTypes(operation, "GET").Should().BeEquivalentTo(contentTypes); 69 | } 70 | 71 | [Fact] 72 | public void GetNotes_ReturnsFallbackRouteNotes_FromSettings() 73 | { 74 | const string notes = "this is a good route"; 75 | using (DocumenterSettings.With(fallbackRouteNotes: notes)) 76 | fallback.GetNotes(operation, "GET").Should().Be(notes); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Extensions/AccessExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Extensions 6 | { 7 | using DataAnnotations; 8 | using FluentAssertions; 9 | using Infrastructure; 10 | using IntroSpec.Extensions; 11 | using Xunit; 12 | 13 | public class AccessExtensionsTests 14 | { 15 | [Fact] 16 | public void CanAccess_True_IfRestrictAttributeNull() 17 | { 18 | RestrictAttribute attr = null; 19 | attr.CanAccess(Result.Success(RequestAttributes.Csv)).Should().BeTrue(); 20 | } 21 | 22 | [Fact] 23 | public void CanAccess_True_IfRestrictAttributeHasAccessToRequestAttribute() 24 | { 25 | var attr = new RestrictAttribute(RequestAttributes.Csv); 26 | attr.CanAccess(Result.Success(RequestAttributes.Csv)).Should().BeTrue(); 27 | } 28 | 29 | [Fact] 30 | public void CanAccess_False_IfRestrictAttributeDoesNotHaveAccessToRequestAttribute() 31 | { 32 | var attr = new RestrictAttribute(RequestAttributes.Html); 33 | attr.CanAccess(Result.Success(RequestAttributes.Csv)).Should().BeFalse(); 34 | } 35 | 36 | [Fact] 37 | public void HasAccessToFeature_True_IfFeatureNotParsedSuccessfully() 38 | => typeof(int).HasAccessToFeature(Result.Fail()).Should().BeTrue(); 39 | 40 | [Fact] 41 | public void HasAccessToFeature_True_IfTypeHasNoExcludeAttribute() 42 | => typeof(NoExclude).HasAccessToFeature(Result.Success(Feature.Html)).Should().BeTrue(); 43 | 44 | [Fact] 45 | public void HasAccessToFeature_True_IfTypeHasExcludeAttributeWithDifferentFeature() 46 | => typeof(HasExclude).HasAccessToFeature(Result.Success(Feature.Html)).Should().BeTrue(); 47 | 48 | [Fact] 49 | public void HasAccessToFeature_False_IfTypeHasExcludeAttributeWithFeature() 50 | => typeof(HasExclude).HasAccessToFeature(Result.Success(Feature.Csv)).Should().BeFalse(); 51 | } 52 | 53 | [Exclude(Feature.Csv)] 54 | public class HasExclude { } 55 | 56 | public class NoExclude { } 57 | } 58 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Extensions/GetValueExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Extensions 6 | { 7 | using System; 8 | using FluentAssertions; 9 | using IntroSpec.Extensions; 10 | using Xunit; 11 | 12 | public class GetValueExtensionsTests 13 | { 14 | [Theory] 15 | [InlineData(null)] 16 | [InlineData("")] 17 | public void GetIfNullOrEmpty_ReturnsValueFromFunc_IfNullOrEmpty(string str) 18 | { 19 | var newVal = Guid.NewGuid().ToString(); 20 | var actual = str.GetIfNullOrEmpty(() => newVal); 21 | 22 | actual.Should().Be(actual); 23 | } 24 | 25 | [Fact] 26 | public void GetIfNullOrEmpty_ReturnsValue_IfNotNullOrEmpty() 27 | { 28 | var str = Guid.NewGuid().ToString(); 29 | str.GetIfNullOrEmpty(() => "hi").Should().Be(str); 30 | } 31 | 32 | [Fact] 33 | public void GetIfNull_ReturnsValueFromFunc_IfNull() 34 | { 35 | string nullObject = null; 36 | var newString = "irnbru"; 37 | 38 | var actual = nullObject.GetIfNull(() => newString); 39 | actual.Should().Be(newString); 40 | } 41 | 42 | [Fact] 43 | public void GetIfNull_ReturnsValue_IfNotNull() 44 | { 45 | var str = Guid.NewGuid().ToString(); 46 | str.GetIfNull(() => "hi").Should().Be(str); 47 | } 48 | 49 | [Fact] 50 | public void GetIfNullOrEmpty_Array_ReturnsValueFromFunc_IfNull() 51 | { 52 | int[] array = null; 53 | var newArray = new[] { 5, 46 }; 54 | 55 | var actual = array.GetIfNullOrEmpty(() => newArray); 56 | actual.Should().BeEquivalentTo(newArray); 57 | } 58 | 59 | [Fact] 60 | public void GetIfNullOrEmpty_Array_ReturnsValueFromFunc_IfEmpty() 61 | { 62 | int[] array = new int[0]; 63 | var newArray = new[] { 5, 46 }; 64 | 65 | var actual = array.GetIfNullOrEmpty(() => newArray); 66 | actual.Should().BeEquivalentTo(newArray); 67 | } 68 | 69 | [Fact] 70 | public void GetIfNullOrEmpty_Array_ReturnsValue_IfNotNull() 71 | { 72 | var array = new[] { 5, 46 }; 73 | 74 | var actual = array.GetIfNullOrEmpty(() => new[] { 4, 3 }); 75 | actual.Should().BeEquivalentTo(array); 76 | } 77 | 78 | [Fact] 79 | public void GetIfNoValue_ReturnsValueFromFunc_IfNull() 80 | { 81 | int? value = null; 82 | int? newValue = 123; 83 | 84 | var actual = value.GetIfNoValue(() => newValue); 85 | actual.Should().Be(newValue); 86 | } 87 | 88 | [Fact] 89 | public void GetIfNoValue_ReturnsValue_IfHasValue() 90 | { 91 | int? value = 123; 92 | 93 | var actual = value.GetIfNoValue(() => (int?) 999); 94 | actual.Should().Be(value); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Extensions/StringExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Extensions 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Extensions; 9 | using Xunit; 10 | 11 | public class StringExtensionsTests 12 | { 13 | [Theory] 14 | [InlineData(null)] 15 | [InlineData("")] 16 | [InlineData(" ")] 17 | public void ToSpaced_EmptyString_IfNullOrWhitespace(string text) => text.ToSpaced().Should().BeEmpty(); 18 | 19 | [Theory] 20 | [InlineData("test", "test")] 21 | [InlineData("testString", "test String")] 22 | [InlineData("TestString", "Test String")] 23 | [InlineData("MultiplePascalCaseWords", "Multiple Pascal Case Words")] 24 | [InlineData("OK", "OK")] 25 | [InlineData("TestOKString", "Test OK String")] 26 | [InlineData("TestOK", "Test OK")] 27 | public void ToSpaced_CorrectResult(string text, string expected) => text.ToSpaced().Should().Be(expected); 28 | 29 | [Theory] 30 | [InlineData(null)] 31 | [InlineData("")] 32 | public void TrimStart_ReturnsString_IfNullOrEmpty(string text) => text.TrimStart("aa").Should().Be(text); 33 | 34 | [Fact] 35 | public void TrimStart_ReturnsString_IfNotStartWith() 36 | { 37 | const string toRemove = "ab"; 38 | const string str = "banana"; 39 | str.TrimStart(toRemove).Should().Be(str); 40 | } 41 | 42 | [Theory] 43 | [InlineData("banana", "b", "anana")] 44 | [InlineData("banana", "ban", "ana")] 45 | [InlineData("banana", "banana", "")] 46 | public void TrimStart_TrimsIfStringStartsWith(string text, string toTrim, string expected) 47 | => text.TrimStart(toTrim).Should().Be(expected); 48 | 49 | [Theory] 50 | [InlineData(null)] 51 | [InlineData("")] 52 | public void EnsureEndsWith_ReturnsString_IfNullOrEmpty(string text) => text.EnsureEndsWith("aa").Should().Be(text); 53 | 54 | [Fact] 55 | public void EnsureEndsWith_ReturnsString_IfNotEndsWith() 56 | { 57 | const string toEndWith = "na"; 58 | const string str = "banana"; 59 | str.EnsureEndsWith(toEndWith).Should().Be(str); 60 | } 61 | 62 | [Fact] 63 | public void EnsureEndsWith_AppendsString_IfDoesntEndWith() 64 | { 65 | const string toEndWith = "app"; 66 | const string str = "banana"; 67 | str.EnsureEndsWith(toEndWith).Should().Be("bananaapp"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Extensions/UrlExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Extensions 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Extensions; 9 | using Xunit; 10 | 11 | public class UrlExtensionsTests 12 | { 13 | [Theory] 14 | [InlineData(null)] 15 | [InlineData("")] 16 | [InlineData(" ")] 17 | [InlineData("/url")] 18 | [InlineData("/url/multi/level")] 19 | public void HasPathParams_ReturnsEmptyList_IfNoPathParams(string url) => url.GetPathParams().Should().BeEmpty(); 20 | 21 | [Theory] 22 | [InlineData("/{foo}")] 23 | [InlineData("/{foo}/")] 24 | [InlineData("/url/{foo}/level")] 25 | [InlineData("/url/multi/{foo}/")] 26 | [InlineData("/url/multi/{foo}")] 27 | public void HasPathParams_ReturnsSinglePathParams(string url) 28 | { 29 | var result = url.GetPathParams(); 30 | result.Count.Should().Be(1); 31 | result[0].Should().Be("foo"); 32 | } 33 | 34 | [Theory] 35 | [InlineData("/{foo}/{bar}")] 36 | [InlineData("/{foo}/{bar}/")] 37 | [InlineData("/url/{foo}/{bar}/level")] 38 | [InlineData("/{foo}/multi/{bar}/")] 39 | [InlineData("/{foo}/multi/{bar}")] 40 | public void HasPathParams_ReturnsMultiPathParams(string url) 41 | { 42 | var result = url.GetPathParams(); 43 | result.Count.Should().Be(2); 44 | result[0].Should().Be("foo"); 45 | result[1].Should().Be("bar"); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Fixtures/AppHostCollection.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Fixtures 6 | { 7 | using Xunit; 8 | 9 | [CollectionDefinition("AppHost")] 10 | public class AppHostCollection : ICollectionFixture 11 | { 12 | // marker class for tests that require apphost.init 13 | // http://xunit.github.io/docs/shared-context.html 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Fixtures/AppHostFixture.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Fixtures 6 | { 7 | using System; 8 | using Metadata; 9 | using Testing; 10 | 11 | public class AppHostFixture : IDisposable 12 | { 13 | public BasicAppHost AppHost { get; set; } 14 | 15 | public const string WebHostUrl = "http://127.0.0.1:8090"; 16 | public const string ServiceName = "testService"; 17 | 18 | public AppHostFixture() 19 | { 20 | var hostConfig = new HostConfig 21 | { 22 | WebHostUrl = WebHostUrl, 23 | DebugMode = true 24 | }; 25 | 26 | AppHost = new BasicAppHost 27 | { 28 | TestMode = true, 29 | Config = hostConfig, 30 | ServiceName = ServiceName 31 | }; 32 | 33 | AppHost.Plugins.Add(new MetadataFeature()); 34 | 35 | AppHost.Init(); 36 | AppHost.Config.WebHostUrl = WebHostUrl; 37 | var hal = "hal+json"; 38 | 39 | HostContext.MetadataPagesConfig.AvailableFormatConfigs.Add(new MetadataConfig(hal, hal, 40 | hal, hal, hal)); 41 | } 42 | 43 | public void Dispose() => AppHost?.Dispose(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Models/PermissionsTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Models 6 | { 7 | using System.Collections.Generic; 8 | using FluentAssertions; 9 | using IntroSpec.Models; 10 | using Xunit; 11 | 12 | public class PermissionsTests 13 | { 14 | public static IEnumerable NullEmpty => new[] 15 | { 16 | new object[] { null, null }, 17 | new object[] { null, new List() }, 18 | new object[] { new List(), null }, 19 | new object[] { new List(), new List() } 20 | }; 21 | 22 | public static TheoryData> SingleNullEmpty = new TheoryData> { null, new List() }; 23 | 24 | [Theory] 25 | [MemberData("NullEmpty")] 26 | public void Create_ReturnsNull_IfPassedBothNulls(IList any, IList all) 27 | => Permissions.Create(any, all).Should().BeNull(); 28 | 29 | [Theory] 30 | [MemberData("SingleNullEmpty")] 31 | public void Create_ReturnsAny_IfAllNullOrEmpty(IList theOther) 32 | { 33 | var list = new List { "foo" }; 34 | var permission = Permissions.Create(list, theOther); 35 | 36 | permission.AnyOf.Should().BeEquivalentTo(list); 37 | permission.AllOf.Should().BeNull(); 38 | } 39 | 40 | [Theory] 41 | [MemberData("SingleNullEmpty")] 42 | public void Create_ReturnsAll_IfAnyNullOrEmpty(IList theOther) 43 | { 44 | var list = new[] { "foo" }; 45 | var permission = Permissions.Create(theOther, list); 46 | 47 | permission.AllOf.Should().BeEquivalentTo(list); 48 | permission.AnyOf.Should().BeNull(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Models/PropertyConstraintTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Models 6 | { 7 | using System; 8 | using FluentAssertions; 9 | using IntroSpec.Models; 10 | using Xunit; 11 | 12 | public class PropertyConstraintTests 13 | { 14 | private const string name = "TheName"; 15 | 16 | [Fact] 17 | public void CreateRangeConstraint_Throws_IfMinAndMaxNull() 18 | { 19 | Action action = () => PropertyConstraint.RangeConstraint(name, null, null); 20 | action.Should().Throw().WithMessage("You must supply either a Min or Max value"); 21 | } 22 | 23 | [Fact] 24 | public void CreateRangeConstraint_Throws_IfMinGreaterThanMax() 25 | { 26 | Action action = () => PropertyConstraint.RangeConstraint(name, 10, 9); 27 | action.Should().Throw(); 28 | } 29 | 30 | [Theory] 31 | [InlineData(null, 40)] 32 | [InlineData(12, null)] 33 | [InlineData(2, 20)] 34 | [InlineData(10, 10)] 35 | public void CreateRangeConstraint_CreatesConstraint_WithCorrectValues(int? min, int? max) 36 | { 37 | var constraint = PropertyConstraint.RangeConstraint(name, min, max); 38 | constraint.Name.Should().Be(name); 39 | constraint.Min.Should().Be(min); 40 | constraint.Max.Should().Be(max); 41 | constraint.Type.Should().Be(ConstraintType.Range); 42 | constraint.Values.Should().BeNull(); 43 | } 44 | 45 | [Theory] 46 | [MemberData("InvalidValuesArray")] 47 | public void CreateListConstraint_Throws_IfValuesNullOrEmpty(string[] values) 48 | { 49 | Action action = () => PropertyConstraint.ListConstraint(name, values); 50 | action.Should().Throw(); 51 | } 52 | 53 | [Fact] 54 | public void CreateListConstraint_CreatesConstraint_WithCorrectValues() 55 | { 56 | var values = new[] { "one", "six" }; 57 | var constraint = PropertyConstraint.ListConstraint(name, values); 58 | constraint.Name.Should().Be(name); 59 | constraint.Min.Should().NotHaveValue(); 60 | constraint.Max.Should().NotHaveValue(); 61 | constraint.Type.Should().Be(ConstraintType.List); 62 | constraint.Values.Should().BeEquivalentTo(values); 63 | } 64 | 65 | public static TheoryData InvalidValuesArray = new TheoryData { null, new string[0] }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Models/RelativePathTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Models 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Models; 9 | using Xunit; 10 | 11 | public class RelativePathTests 12 | { 13 | [Fact] 14 | public void ImplicitFromStringCast_SetsPath() 15 | { 16 | const string path = "/api/foo"; 17 | 18 | RelativePath relativePath = path; 19 | relativePath.Path.Should().Be(path); 20 | } 21 | 22 | [Fact] 23 | public void ImplicitToStringCast_ReturnsPath() 24 | { 25 | const string path = "/api/foo"; 26 | 27 | var relativePath = new RelativePath { Path = path }; 28 | string actual = relativePath; 29 | 30 | actual.Should().Be(path); 31 | } 32 | 33 | [Fact] 34 | public void IsAutoRoute_True_IfAutoRoute() 35 | { 36 | var relativePath = new RelativePath { Source = Constants.RouteSources.AutoRoute }; 37 | 38 | relativePath.IsAutoRoute.Should().BeTrue(); 39 | } 40 | 41 | [Theory] 42 | [InlineData("")] 43 | [InlineData(null)] 44 | [InlineData(Constants.RouteSources.Attribute)] 45 | public void IsAutoRoute_False_IfNotAutoRoute(string source) 46 | { 47 | var relativePath = new RelativePath { Source = source }; 48 | 49 | relativePath.IsAutoRoute.Should().BeFalse(); 50 | } 51 | 52 | [Fact] 53 | public void IsFallbackRoute_True_IfFallback() 54 | { 55 | var relativePath = new RelativePath { Source = Constants.RouteSources.FallbackRoute }; 56 | 57 | relativePath.IsFallback.Should().BeTrue(); 58 | } 59 | 60 | [Theory] 61 | [InlineData("")] 62 | [InlineData(null)] 63 | [InlineData(Constants.RouteSources.AutoRoute)] 64 | public void IsFallbackRoute_False_IfNotFallback(string source) 65 | { 66 | var relativePath = new RelativePath { Source = source }; 67 | 68 | relativePath.IsFallback.Should().BeFalse(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Models/StatusCodesTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Models 6 | { 7 | using System.Net; 8 | using FluentAssertions; 9 | using IntroSpec.Models; 10 | using Xunit; 11 | 12 | public class StatusCodesTests 13 | { 14 | [Fact] 15 | public void ImplicitHttpStatusCodeCast_SetsStatus() 16 | { 17 | var statusCode = (StatusCode)HttpStatusCode.Forbidden; 18 | statusCode.Code.Should().Be(403); 19 | } 20 | 21 | [Fact] 22 | public void ImplicitHttpStatusCodeCast_SetsName() 23 | { 24 | var statusCode = (StatusCode)HttpStatusCode.Forbidden; 25 | statusCode.Name.Should().Be("Forbidden"); 26 | } 27 | 28 | [Fact] 29 | public void ImplicitHttpStatusCodeCast_SetsNameFormatted() 30 | { 31 | var statusCode = (StatusCode)HttpStatusCode.GatewayTimeout; 32 | statusCode.Name.Should().Be("Gateway Timeout"); 33 | } 34 | 35 | [Fact] 36 | public void ImplicitHttpStatusCodeCast_DoesNotSetDescription() 37 | { 38 | var statusCode = (StatusCode)HttpStatusCode.Forbidden; 39 | statusCode.Description.Should().BeNull(); 40 | } 41 | 42 | [Fact] 43 | public void ImplicitIntCast_SetsStatus() 44 | { 45 | var statusCode = (StatusCode)403; 46 | statusCode.Code.Should().Be(403); 47 | } 48 | 49 | [Fact] 50 | public void ImplicitIntCast_SetsName() 51 | { 52 | var statusCode = (StatusCode)403; 53 | statusCode.Name.Should().Be("Forbidden"); 54 | } 55 | 56 | [Fact] 57 | public void ImplicitIntCast_SetsNameFormatted() 58 | { 59 | var statusCode = (StatusCode)504; 60 | statusCode.Name.Should().Be("Gateway Timeout"); 61 | } 62 | 63 | [Fact] 64 | public void ImplicitIntCast_DoesNotSetDescription() 65 | { 66 | var statusCode = (StatusCode)504; 67 | statusCode.Description.Should().BeNull(); 68 | } 69 | 70 | [Fact] 71 | public void Equals_True_SameStatusCode() 72 | { 73 | var code = new StatusCode { Code = 201, Description = "One", Name = "Strong" }; 74 | var code2 = new StatusCode { Code = 201, Description = "Two", Name = "Weak" }; 75 | 76 | code.Equals(code2).Should().BeTrue(); 77 | } 78 | 79 | [Fact] 80 | public void Equals_False_DifferentStatusCode() 81 | { 82 | var code = new StatusCode { Code = 204 }; 83 | var code2 = new StatusCode { Code = 201 }; 84 | 85 | code.Equals(code2).Should().BeFalse(); 86 | } 87 | 88 | [Fact] 89 | public void WithDescription_SetsDescription() 90 | { 91 | const string desc = "test description"; 92 | var code = new StatusCode().WithDescription(desc); 93 | code.Description.Should().Be(desc); 94 | } 95 | 96 | [Fact] 97 | public void WithName_SetsName() 98 | { 99 | const string name = "john"; 100 | var code = new StatusCode().WithName(name); 101 | code.Name.Should().Be(name); 102 | } 103 | 104 | [Fact] 105 | public void WithCode_SetsCode() 106 | { 107 | const int code = 200; 108 | var statusCode = new StatusCode().WithCode(code); 109 | statusCode.Code.Should().Be(code); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/ResultTests/ResultTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.ResultTests 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Infrastructure; 9 | using Xunit; 10 | 11 | public class ResultTests 12 | { 13 | [Fact] 14 | public void Fail_IsSuccess_False() => Result.Fail().IsSuccess.Should().BeFalse(); 15 | 16 | [Fact] 17 | public void Fail_Value_DefaultT() => Result.Fail().Value.Should().Be(default(string)); 18 | 19 | [Fact] 20 | public void Fail_WithValue_IsSuccess() => Result.Fail("dance").IsSuccess.Should().BeFalse(); 21 | 22 | [Fact] 23 | public void Fail_WithValue_Value() => Result.Fail("dance").Value.Should().Be("dance"); 24 | 25 | [Fact] 26 | public void Success_IsSuccess_True() => Result.Success("wallop").IsSuccess.Should().BeTrue(); 27 | 28 | [Fact] 29 | public void Success_Value() => Result.Success("wallop").Value.Should().Be("wallop"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/ServiceStack.IntroSpec.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Settings/ApiSpecConfigTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Settings 6 | { 7 | using System; 8 | using FluentAssertions; 9 | using IntroSpec.Settings; 10 | using Xunit; 11 | 12 | public class ApiSpecConfigTests 13 | { 14 | [Fact] 15 | public void Ctor_SetsDefaultContact() 16 | => new ApiSpecConfig().Contact.Should().NotBeNull(); 17 | 18 | [Fact] 19 | public void WithBasicDetails_SetsDetails() 20 | { 21 | const string description = "Description of it"; 22 | var uri = new Uri("http://acme.test"); 23 | 24 | var spec = new ApiSpecConfig().WithLicenseUrl(uri).WithDescription(description); 25 | spec.Description.Should().Be(description); 26 | spec.LicenseUrl.Should().Be(uri); 27 | } 28 | 29 | [Fact] 30 | public void WithContactDetails_SetsContactDetails() 31 | { 32 | const string name = "Test Name"; 33 | var uri = new Uri("http://acme.test"); 34 | const string email = "test@example.com"; 35 | 36 | var spec = new ApiSpecConfig().WithContactName(name).WithContactUrl(uri).WithContactEmail(email); 37 | 38 | var contact = spec.Contact; 39 | contact.Name.Should().Be(name); 40 | contact.Url.Should().Be(uri.OriginalString); 41 | contact.Email.Should().Be(email); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/TypeSpec/AbstractRequestSpecTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.TypeSpec 6 | { 7 | using FluentAssertions; 8 | using Html; 9 | using IntroSpec.Models; 10 | using IntroSpec.TypeSpec; 11 | using Xunit; 12 | 13 | public class AbstractRequestSpecTests 14 | { 15 | private const string GlobalKey = "_all"; 16 | private RequestDocumenter documenter = new RequestDocumenter(); 17 | 18 | [Fact] 19 | public void Ctor_InitialisesLists() 20 | { 21 | var d = new RequestDocumenter(); 22 | d.StatusCodes.Should().NotBeNull(); 23 | d.Tags.Should().NotBeNull(); 24 | d.ContentTypes.Should().NotBeNull(); 25 | } 26 | 27 | [Fact] 28 | public void AddTags_PopulatesTagsCollection() 29 | { 30 | documenter.SetTags("Tag1", "Tag2"); 31 | documenter.Tags.Count.Should().Be(2); 32 | documenter.Tags.Should().Contain("Tag1").And.Contain("Tag2"); 33 | } 34 | 35 | [Fact] 36 | public void AddStatusCodes_NoVerb_PopulatesStatusCodesCollection() 37 | { 38 | var code = new StatusCode { Code = 201 }; 39 | var code2 = new StatusCode { Code = 204 }; 40 | documenter.SetStatusCodes(code, code2); 41 | documenter.StatusCodes[GlobalKey].Count.Should().Be(2); 42 | documenter.StatusCodes[GlobalKey].Should().Contain(code).And.Contain(code2); 43 | } 44 | 45 | [Fact] 46 | public void AddStatusCodes_Verb_PopulatesStatusCodesCollection() 47 | { 48 | var verb = HttpVerbs.Get; 49 | var verbString = verb.ToString(); 50 | 51 | var code = new StatusCode { Code = 201 }; 52 | var code2 = new StatusCode { Code = 204 }; 53 | documenter.SetStatusCodes(verb, code, code2); 54 | documenter.StatusCodes[verbString].Count.Should().Be(2); 55 | documenter.StatusCodes[verbString].Should().Contain(code).And.Contain(code2); 56 | } 57 | 58 | [Fact] 59 | public void AddContentTypes_NoVerb_PopulatesContentTypesCollection() 60 | { 61 | documenter.SetContentTypes("text/json", "text/xml"); 62 | documenter.ContentTypes[GlobalKey].Count.Should().Be(2); 63 | documenter.ContentTypes[GlobalKey].Should().Contain("text/json").And.Contain("text/xml"); 64 | } 65 | 66 | [Fact] 67 | public void AddContentTypes_Verb_PopulatesContentTypesCollection() 68 | { 69 | var verb = HttpVerbs.Get; 70 | var verbString = verb.ToString(); 71 | documenter.SetContentTypes(verb, "text/json", "text/xml"); 72 | 73 | documenter.ContentTypes[verbString].Count.Should().Be(2); 74 | documenter.ContentTypes[verbString].Should().Contain("text/json").And.Contain("text/xml"); 75 | } 76 | 77 | [Fact] 78 | public void AddRouteNotes_NoVerb_PopulatesRouteNotesCollection() 79 | { 80 | const string notes = "foo bar baz"; 81 | documenter.SetNotes(notes); 82 | documenter.RouteNotes[GlobalKey].Should().Be(notes); 83 | } 84 | 85 | [Fact] 86 | public void AddRouteNotes_Verb_PopulatesRouteNotesCollection() 87 | { 88 | var verb = HttpVerbs.Get; 89 | var verbString = verb.ToString(); 90 | const string notes = "foo bar baz"; 91 | documenter.SetNotes(verb, notes); 92 | documenter.RouteNotes[verbString].Should().Be(notes); 93 | } 94 | } 95 | 96 | internal class RequestDocumenter : AbstractRequestSpec 97 | { 98 | internal void SetTags(params string[] tags) => AddTags(tags); 99 | 100 | internal void SetStatusCodes(params StatusCode[] codes) => AddStatusCodes(codes); 101 | internal void SetStatusCodes(HttpVerbs verb, params StatusCode[] codes) => AddStatusCodes(verb, codes); 102 | 103 | internal void SetContentTypes(params string[] contentTypes) => AddContentTypes(contentTypes); 104 | internal void SetContentTypes(HttpVerbs verb, params string[] contentTypes) => AddContentTypes(verb, contentTypes); 105 | 106 | internal void SetNotes(string notes) => AddRouteNotes(notes); 107 | internal void SetNotes(HttpVerbs verb, string notes) => AddRouteNotes(verb, notes); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/TypeSpec/ApiDtoSpecTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.TypeSpec 6 | { 7 | using System; 8 | using System.Reflection; 9 | using FluentAssertions; 10 | using IntroSpec.TypeSpec; 11 | using Xunit; 12 | 13 | public class ApiDtoSpecTests 14 | { 15 | private PropertyInfo propertyInfo; 16 | private Type testType; 17 | 18 | public ApiDtoSpecTests() 19 | { 20 | testType = typeof (ToDocument); 21 | propertyInfo = testType.GetProperty("Name"); 22 | } 23 | 24 | [Fact] 25 | public void GetPropertySpec_ReturnsNull_IfNotFound() 26 | => new Documenter().GetPropertySpec(propertyInfo).Should().BeNull(); 27 | 28 | [Fact] 29 | public void For_AddsParameterToLookup() 30 | => new Documenter(true).GetPropertySpec(propertyInfo).Should().NotBeNull(); 31 | 32 | [Fact] 33 | public void For_AddsParameterToLookup_WithCorrectValues() 34 | { 35 | var prop = new Documenter(true).GetPropertySpec(propertyInfo); 36 | 37 | prop.Title.Should().Be("The Name"); 38 | prop.IsRequired.Should().BeTrue(); 39 | prop.Description.Should().Be("Description of name"); 40 | } 41 | } 42 | 43 | public class Documenter : AbstractTypeSpec 44 | { 45 | public Documenter() {} 46 | public Documenter(bool addPropertyValue) 47 | { 48 | if (addPropertyValue) 49 | { 50 | For(p => p.Name) 51 | .With(t => t.Title, "The Name") 52 | .With(t => t.IsRequired, true) 53 | .With(t => t.Description, "Description of name"); 54 | } 55 | } 56 | } 57 | 58 | public class ToDocument 59 | { 60 | public string Name { get; set; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/TypeSpec/DocumentationClassLocatorTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.TypeSpec 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Settings; 9 | using IntroSpec.TypeSpec; 10 | using Xunit; 11 | 12 | public class DocumentationClassLocatorTests 13 | { 14 | [Fact] 15 | public void GetLookup_FindsDirectDescendant() 16 | { 17 | using (DocumenterSettings.With(assemblies: new[] { typeof (ClassToDocument).Assembly })) 18 | { 19 | var lookup = DocumentationClassLocator.GetLookup(); 20 | var value = lookup[typeof (ClassToDocument)]; 21 | value.Should().BeOfType(); 22 | } 23 | } 24 | 25 | [Fact] 26 | public void GetLookup_FindsDeeperDescendant() 27 | { 28 | using (DocumenterSettings.With(assemblies: new[] { typeof(ClassToDocument).Assembly })) 29 | { 30 | var lookup = DocumentationClassLocator.GetLookup(); 31 | var value = lookup[typeof(AnotherClassToDocument)]; 32 | value.Should().BeOfType(); 33 | } 34 | } 35 | 36 | [Fact] 37 | public void GetLookup_DoesNotReturn_NonInheriter() 38 | { 39 | using (DocumenterSettings.With(assemblies: new[] { typeof(ClassToDocument).Assembly })) 40 | { 41 | var lookup = DocumentationClassLocator.GetLookup(); 42 | lookup.ContainsKey(typeof (NotDocumented)).Should().BeFalse(); 43 | } 44 | } 45 | } 46 | 47 | public class ClassToDocument { } 48 | public class AnotherClassToDocument { } 49 | public class NotDocumented { } 50 | 51 | public class DirectDescendant : AbstractTypeSpec { } 52 | public class DeeperDescendant : AbstractRequestSpec { } 53 | } 54 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/TypeSpec/PropertyMetadataTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.TypeSpec 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Models; 9 | using IntroSpec.TypeSpec; 10 | using Xunit; 11 | 12 | public class PropertyMetadataTests 13 | { 14 | private static PropertyMetadata GetPropertyMetadata() 15 | => new PropertyMetadata(typeof (MetadataTestClass).GetProperty("Name")); 16 | 17 | [Fact] 18 | public void With_ReturnsSelf() 19 | { 20 | var metadata = GetPropertyMetadata(); 21 | var result = metadata.With(x => x.Title, "whatever"); 22 | 23 | result.Should().Be(metadata); 24 | } 25 | 26 | [Fact] 27 | public void With_SetsTitle() 28 | { 29 | var metadata = GetPropertyMetadata(); 30 | const string title = "My Title"; 31 | metadata.With(x => x.Title, title); 32 | 33 | metadata.Title.Should().Be(title); 34 | } 35 | 36 | [Fact] 37 | public void With_SetsDescription() 38 | { 39 | var metadata = GetPropertyMetadata(); 40 | const string desc = "My Generation"; 41 | metadata.With(x => x.Description, desc); 42 | 43 | metadata.Description.Should().Be(desc); 44 | } 45 | 46 | [Fact] 47 | public void With_SetsIsRequired() 48 | { 49 | var metadata = GetPropertyMetadata(); 50 | metadata.With(x => x.IsRequired, true); 51 | 52 | metadata.IsRequired.Should().BeTrue(); 53 | } 54 | 55 | [Fact] 56 | public void With_SetsConstraint() 57 | { 58 | var metadata = GetPropertyMetadata(); 59 | var propertyConstraint = PropertyConstraint.RangeConstraint("Test constraint", 1, 2); 60 | metadata.With(x => x.Constraint, propertyConstraint); 61 | 62 | metadata.Constraint.Should().Be(propertyConstraint); 63 | } 64 | 65 | [Fact] 66 | public void With_SetsAllowMultiple() 67 | { 68 | var metadata = GetPropertyMetadata(); 69 | metadata.With(x => x.AllowMultiple, true); 70 | 71 | metadata.AllowMultiple.Should().BeTrue(); 72 | } 73 | } 74 | 75 | public class MetadataTestClass 76 | { 77 | public string Name { get; set; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Utilities/EnumUtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Utilities 6 | { 7 | using System; 8 | using FluentAssertions; 9 | using IntroSpec.Utilities; 10 | using Xunit; 11 | 12 | public class EnumUtilitiesTests 13 | { 14 | [Fact] 15 | public void SafeParse_Fail_IfNotEnum() 16 | => EnumUtilities.SafeParse("wa").IsSuccess.Should().BeFalse(); 17 | 18 | [Fact] 19 | public void SafeParse_Fail_IfValueNotFound() 20 | => EnumUtilities.SafeParse("Three").IsSuccess.Should().BeFalse(); 21 | 22 | [Theory] 23 | [InlineData("two")] 24 | [InlineData("Two")] 25 | public void SafeParse_Success_IfValueNotFound_CaseInsensitive(string value) 26 | => EnumUtilities.SafeParse(value).IsSuccess.Should().BeTrue(); 27 | 28 | [Fact] 29 | public void SafeParse_CorrectValue_IfValueNotFound_CaseInsensitive() 30 | => EnumUtilities.SafeParse("two").Value.Should().Be(TestEnum.Two); 31 | } 32 | 33 | public enum TestEnum 34 | { 35 | One, 36 | Two 37 | } 38 | 39 | public struct FakeEnum : IConvertible 40 | { 41 | public TypeCode GetTypeCode() 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | 46 | public bool ToBoolean(IFormatProvider provider) 47 | { 48 | throw new NotImplementedException(); 49 | } 50 | 51 | public char ToChar(IFormatProvider provider) 52 | { 53 | throw new NotImplementedException(); 54 | } 55 | 56 | public sbyte ToSByte(IFormatProvider provider) 57 | { 58 | throw new NotImplementedException(); 59 | } 60 | 61 | public byte ToByte(IFormatProvider provider) 62 | { 63 | throw new NotImplementedException(); 64 | } 65 | 66 | public short ToInt16(IFormatProvider provider) 67 | { 68 | throw new NotImplementedException(); 69 | } 70 | 71 | public ushort ToUInt16(IFormatProvider provider) 72 | { 73 | throw new NotImplementedException(); 74 | } 75 | 76 | public int ToInt32(IFormatProvider provider) 77 | { 78 | throw new NotImplementedException(); 79 | } 80 | 81 | public uint ToUInt32(IFormatProvider provider) 82 | { 83 | throw new NotImplementedException(); 84 | } 85 | 86 | public long ToInt64(IFormatProvider provider) 87 | { 88 | throw new NotImplementedException(); 89 | } 90 | 91 | public ulong ToUInt64(IFormatProvider provider) 92 | { 93 | throw new NotImplementedException(); 94 | } 95 | 96 | public float ToSingle(IFormatProvider provider) 97 | { 98 | throw new NotImplementedException(); 99 | } 100 | 101 | public double ToDouble(IFormatProvider provider) 102 | { 103 | throw new NotImplementedException(); 104 | } 105 | 106 | public decimal ToDecimal(IFormatProvider provider) 107 | { 108 | throw new NotImplementedException(); 109 | } 110 | 111 | public DateTime ToDateTime(IFormatProvider provider) 112 | { 113 | throw new NotImplementedException(); 114 | } 115 | 116 | public string ToString(IFormatProvider provider) 117 | { 118 | throw new NotImplementedException(); 119 | } 120 | 121 | public object ToType(Type conversionType, IFormatProvider provider) 122 | { 123 | throw new NotImplementedException(); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Utilities/MimeTypeUtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Utilities 6 | { 7 | using FluentAssertions; 8 | using IntroSpec.Utilities; 9 | using Xunit; 10 | 11 | public class MimeTypeUtilitiesTests 12 | { 13 | [Theory] 14 | [InlineData("Soap11", "text/xml; charset=utf-8")] 15 | [InlineData("msgPack", "application/x-msgpack")] 16 | public void GetMimeType_ReturnsKnownType(string type, string expected) 17 | => MimeTypeUtilities.GetMimeType(type).Should().Be(expected); 18 | 19 | [Fact] 20 | public void GetMimeType_ReturnsDefault_IfNotKnownType() 21 | => MimeTypeUtilities.GetMimeType("hal+json").Should().Be("application/hal+json"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/Validators/ApiSpecSettingsValidatorTest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.Validators 6 | { 7 | using System; 8 | using FluentValidation.TestHelper; 9 | using IntroSpec.Validators; 10 | using Xunit; 11 | 12 | public class ApiSpecSettingsValidatorTest 13 | { 14 | private readonly ApiSpecSettingsValidator validator = new ApiSpecSettingsValidator(); 15 | 16 | [Theory] 17 | [InlineData(null)] 18 | [InlineData("")] 19 | [InlineData(" ")] 20 | public void ContactName_IsMandatory(string name) 21 | { 22 | validator.ShouldHaveValidationErrorFor(v => v.ContactName, new TestSettings { ContactName = name }); 23 | } 24 | 25 | [Theory] 26 | [InlineData(null)] 27 | [InlineData("")] 28 | [InlineData(" ")] 29 | public void ContactEmail_IsMandatory(string email) 30 | { 31 | validator.ShouldHaveValidationErrorFor(v => v.ContactEmail, new TestSettings { ContactEmail = email }); 32 | } 33 | 34 | [Theory] 35 | [InlineData("test")] 36 | [InlineData("test@example")] 37 | [InlineData("test.example.com")] 38 | public void ContactEmail_MustBeValidEmail(string email) 39 | { 40 | validator.ShouldHaveValidationErrorFor(v => v.ContactEmail, new TestSettings { ContactEmail = email }); 41 | } 42 | 43 | [Theory] 44 | [InlineData(null)] 45 | [InlineData("")] 46 | [InlineData(" ")] 47 | public void Description_IsMandatory(string description) 48 | { 49 | validator.ShouldHaveValidationErrorFor(v => v.Description, new TestSettings { Description = description }); 50 | } 51 | } 52 | 53 | public class TestSettings : IApiSpecSettings 54 | { 55 | public string ContactEmail { get; set; } 56 | public string ContactName { get; set; } 57 | public Uri ContactUrl { get; set; } 58 | public string Description { get; set; } 59 | public Uri LicenseUrl { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.Tests/XmlDocumentation/XmlDocumentationLookupTests.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Tests.XmlDocumentation 6 | { 7 | using System; 8 | using FakeItEasy; 9 | using FluentAssertions; 10 | using IntroSpec.XmlDocumentation; 11 | using Xunit; 12 | 13 | public class XmlDocumentationLookupTests 14 | { 15 | private readonly IXmlDocumentationReader reader = A.Fake(); 16 | 17 | private XmlDocumentationLookup GetSut() => new XmlDocumentationLookup(reader); 18 | 19 | [Fact] 20 | public void Ctor_ThrowsException_IfDocumentationReaderNull() 21 | { 22 | Action action = () => new XmlDocumentationLookup(null); 23 | action.Should().Throw(); 24 | } 25 | 26 | [Fact] 27 | public void Ctor_CallsGetXmlDocumentation() 28 | { 29 | new XmlDocumentationLookup(reader); 30 | A.CallTo(() => reader.GetXmlDocumentation()).MustHaveHappened(); 31 | } 32 | 33 | [Fact] 34 | public void GetXmlMember_ReturnsDefault_IfNullXmlDocumentation() 35 | { 36 | A.CallTo(() => reader.GetXmlDocumentation()).Returns(null); 37 | var sut = GetSut(); 38 | 39 | var result = sut.GetXmlMember(typeof (Type)); 40 | result.Should().Be(XmlMember.Default); 41 | } 42 | 43 | [Fact] 44 | public void GetXmlMember_ReturnsDefault_IfNullXmlMembers() 45 | { 46 | A.CallTo(() => reader.GetXmlDocumentation()).Returns(new XmlDocumentation()); 47 | var sut = GetSut(); 48 | 49 | var result = sut.GetXmlMember(typeof(Type)); 50 | result.Should().Be(XmlMember.Default); 51 | } 52 | 53 | [Fact] 54 | public void GetXmlMember_ReturnsDefault_IfXmlMemberNotFound() 55 | { 56 | var xmlDocumentation = new XmlDocumentation 57 | { 58 | Members = new[] { new XmlMember { Name = "Unfindable" } } 59 | }; 60 | 61 | A.CallTo(() => reader.GetXmlDocumentation()).Returns(xmlDocumentation); 62 | 63 | var sut = GetSut(); 64 | var result = sut.GetXmlMember(typeof(Type)); 65 | result.Should().Be(XmlMember.Default); 66 | } 67 | 68 | [Fact] 69 | public void GetXmlMember_ReturnsXmlMember_IfFound() 70 | { 71 | var xmlMember = new XmlMember { Name = "T:System.Type" }; 72 | var xmlDocumentation = new XmlDocumentation 73 | { 74 | Members = new[] { xmlMember } 75 | }; 76 | 77 | A.CallTo(() => reader.GetXmlDocumentation()).Returns(xmlDocumentation); 78 | 79 | var sut = GetSut(); 80 | var result = sut.GetXmlMember(typeof(Type)); 81 | result.Should().Be(xmlMember); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7654ED39-DF4D-47A5-87E4-F93FC69AD5AD}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{0DA62C09-3532-45FF-AB6C-31B9507972B3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoService", "DemoService\DemoService.csproj", "{8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.IntroSpec.Tests", "ServiceStack.IntroSpec.Tests\ServiceStack.IntroSpec.Tests.csproj", "{0328647A-7C14-4D30-89C7-AFAD6A083984}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.IntroSpec", "ServiceStack.IntroSpec\ServiceStack.IntroSpec.csproj", "{50FAC613-CC23-493B-AEF8-77277D5B3F3C}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntroSpec", "IntroSpec\IntroSpec\IntroSpec.csproj", "{D32EB8EE-3954-453B-A3FD-07C605C86160}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{3717F572-08FE-418F-835C-5BD6D97B3BA1}" 19 | ProjectSection(SolutionItems) = preProject 20 | ..\..\readme.md = ..\..\readme.md 21 | ..\..\docs\settings.md = ..\..\docs\settings.md 22 | ..\..\docs\sources.md = ..\..\docs\sources.md 23 | EndProjectSection 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {0328647A-7C14-4D30-89C7-AFAD6A083984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {0328647A-7C14-4D30-89C7-AFAD6A083984}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {0328647A-7C14-4D30-89C7-AFAD6A083984}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {0328647A-7C14-4D30-89C7-AFAD6A083984}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {50FAC613-CC23-493B-AEF8-77277D5B3F3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {50FAC613-CC23-493B-AEF8-77277D5B3F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {50FAC613-CC23-493B-AEF8-77277D5B3F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {50FAC613-CC23-493B-AEF8-77277D5B3F3C}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {D32EB8EE-3954-453B-A3FD-07C605C86160}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {D32EB8EE-3954-453B-A3FD-07C605C86160}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {D32EB8EE-3954-453B-A3FD-07C605C86160}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {D32EB8EE-3954-453B-A3FD-07C605C86160}.Release|Any CPU.Build.0 = Release|Any CPU 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | GlobalSection(NestedProjects) = preSolution 52 | {0DA62C09-3532-45FF-AB6C-31B9507972B3} = {7654ED39-DF4D-47A5-87E4-F93FC69AD5AD} 53 | {8736F7A8-E8FB-48BE-B6E2-2E439DEE57F2} = {0DA62C09-3532-45FF-AB6C-31B9507972B3} 54 | {0328647A-7C14-4D30-89C7-AFAD6A083984} = {7654ED39-DF4D-47A5-87E4-F93FC69AD5AD} 55 | {D32EB8EE-3954-453B-A3FD-07C605C86160} = {0DA62C09-3532-45FF-AB6C-31B9507972B3} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/ApiDocumentationGenerator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using Enrichers.Interfaces; 11 | using Host; 12 | using Logging; 13 | using Models; 14 | 15 | public class ApiDocumentationGenerator : IApiDocumentationGenerator 16 | { 17 | private readonly ILog log = LogManager.GetLogger(typeof(ApiDocumentationGenerator)); 18 | private readonly Func> getEnrichers; 19 | 20 | public ApiDocumentationGenerator(Func> getEnrichers) 21 | { 22 | this.getEnrichers = getEnrichers; 23 | } 24 | 25 | public ApiDocumentation GenerateDocumentation(IEnumerable operations, IAppHost appHost, IApiSpecSettings settings) 26 | { 27 | var apiDoc = GetApiDocumentation(appHost, settings); 28 | 29 | var resourceDocs = GenerateResourceDocumentation(operations); 30 | apiDoc.Resources = resourceDocs.ToArray(); 31 | 32 | return apiDoc; 33 | } 34 | 35 | private static ApiDocumentation GetApiDocumentation(IAppHost appHost, IApiSpecSettings settings) 36 | { 37 | var apiDoc = new ApiDocumentation 38 | { 39 | Title = (appHost as ServiceStackHost)?.ServiceName, 40 | ApiVersion = appHost.Config?.ApiVersion, 41 | Contact = 42 | new ApiContact 43 | { 44 | Email = settings.ContactEmail, 45 | Name = settings.ContactName, 46 | Url = settings.ContactUrl?.OriginalString 47 | }, 48 | Description = settings.Description, 49 | Plugins = 50 | appHost.Plugins.Select( 51 | x => 52 | new ApiPlugin 53 | { 54 | Name = x.GetType().FullName, 55 | Version = x.GetType().Assembly.GetName().Version.ToString() 56 | }).ToArray() 57 | }; 58 | 59 | if (settings.LicenseUrl != null) 60 | apiDoc.LicenceUrl = settings.LicenseUrl.ToString(); 61 | return apiDoc; 62 | } 63 | 64 | private IEnumerable GenerateResourceDocumentation(IEnumerable operations) 65 | { 66 | var resourceDocs = new List(); 67 | var enrichers = getEnrichers().ToList(); 68 | 69 | foreach (var operation in operations ?? Enumerable.Empty()) 70 | { 71 | log.Debug($"Found type: {operation.RequestType}"); 72 | 73 | var resourceDoc = new ApiResourceDocumentation { TypeName = operation.RequestType.Name }; 74 | 75 | foreach (var apiSpecPopulater in enrichers) 76 | { 77 | log.Debug($"Populating {operation.RequestType} with {apiSpecPopulater.GetType().Name}"); 78 | apiSpecPopulater.Enrich(resourceDoc, operation); 79 | } 80 | 81 | resourceDocs.Add(resourceDoc); 82 | } 83 | 84 | return resourceDocs; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/ApiSpecFeature.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | using System; 8 | using Settings; 9 | 10 | [Obsolete("Use IntroSpecFeature rather than ApiSpecFeature. Maintained for backwards compat.")] 11 | public class ApiSpecFeature : IntroSpecFeature 12 | { 13 | [Obsolete("Use parameterless ctor and set public properties")] 14 | public ApiSpecFeature(ApiConfigDelegate config) 15 | { 16 | var cfg = config(new ApiSpecConfig()); 17 | cfg.ThrowIfNull(nameof(cfg)); 18 | 19 | cfg.PopulateProperties(this); 20 | } 21 | 22 | public ApiSpecFeature() 23 | { 24 | 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/ConfigKeys.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | public static class ConfigKeys 8 | { 9 | private const string KeyPrefix = "servicestack.plugins.introspec."; 10 | public const string ContactName = KeyPrefix + "contact.name"; 11 | public const string ContactEmail = KeyPrefix + "contact.email"; 12 | public const string ContactUrl = KeyPrefix + "contact.url"; 13 | public const string Description = KeyPrefix + "description"; 14 | public const string LicenseUrl = KeyPrefix + "licenseurl"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Constants.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | public static class Constants 8 | { 9 | public const string SpecUri = "/spec"; 10 | public const string SpecSummaryUri = "/spec/summary"; 11 | public const string PostmanSpecUri = "/spec/postman"; 12 | 13 | public const string XmlEnricherKey = "xml"; 14 | public const string ReflectionEnricherKey = "reflection"; 15 | public const string ClassEnricherKey = "abstractclass"; 16 | public const string FallbackEnricherKey = "fallback"; 17 | 18 | public const string GlobalSettingsKey = "_all"; 19 | 20 | public static class RouteSources 21 | { 22 | public const string Attribute = "Attribute"; 23 | public const string AutoRoute = "AutoRoute"; 24 | public const string FallbackRoute = "Fallback"; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/DtoGrouping.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | using System.Collections.Generic; 8 | 9 | public class DtoGrouping 10 | { 11 | public string Key { get; set; } 12 | 13 | public IEnumerable DtoNames { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/IFilterableSpecRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | /// 8 | /// Custom interface used by DTOs that offer filtering 9 | /// 10 | public interface IFilterableSpecRequest 11 | { 12 | string[] DtoNames { get; set; } 13 | string[] Categories { get; set; } 14 | string[] Tags { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/SpecMetadataRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | using DataAnnotations; 8 | 9 | [Route(Constants.SpecSummaryUri)] 10 | [Exclude(Feature.Metadata | Feature.ServiceDiscovery)] 11 | public class SpecMetadataRequest : IReturn 12 | { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/SpecMetadataResponse.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | using System.Collections.Generic; 8 | 9 | public class SpecMetadataResponse 10 | { 11 | public IEnumerable DtoNames { get; set; } 12 | public IEnumerable Categories { get; set; } 13 | public IEnumerable Tags { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/SpecRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | using System.Runtime.Serialization; 8 | using DataAnnotations; 9 | 10 | [Route(Constants.SpecUri)] 11 | [Exclude(Feature.Metadata | Feature.ServiceDiscovery)] 12 | [DataContract] 13 | public class SpecRequest : IReturn, IFilterableSpecRequest 14 | { 15 | [DataMember(Name = "DtoName")] 16 | public string[] DtoNames { get; set; } 17 | 18 | [DataMember(Name = "Category")] 19 | public string[] Categories { get; set; } 20 | 21 | [DataMember(Name = "Tag")] 22 | public string[] Tags { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/DTO/SpecResponse.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.DTO 6 | { 7 | using Models; 8 | 9 | public class SpecResponse 10 | { 11 | public ApiDocumentation ApiDocumentation { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/FallbackEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers 6 | { 7 | using System; 8 | using System.Linq; 9 | using Host; 10 | using Interfaces; 11 | using Models; 12 | using Settings; 13 | 14 | /// 15 | /// Enricher that will use global settings properties to enrich object 16 | /// 17 | public class FallbackEnricher : IResourceEnricher, IRequestEnricher, IActionEnricher 18 | { 19 | public string GetTitle(Type type) => null; 20 | 21 | public string GetDescription(Type type) => null; 22 | 23 | public string GetNotes(Type type) => DocumenterSettings.FallbackNotes; 24 | 25 | public bool? GetAllowMultiple(Type type) => null; 26 | public bool? GetHasValidator(Type type) => null; 27 | 28 | public string[] GetContentTypes(Operation operation, string verb) => DocumenterSettings.DefaultContentTypes?.ToArray(); 29 | 30 | public StatusCode[] GetStatusCodes(Operation operation, string verb) => DocumenterSettings.DefaultStatusCodes?.ToArray(); 31 | 32 | public string GetNotes(Operation operation, string verb) => DocumenterSettings.FallbackRouteNotes; 33 | 34 | public RelativePath[] GetRelativePaths(Operation operation, string verb) => null; 35 | 36 | public string GetCategory(Operation operation) => DocumenterSettings.FallbackCategory; 37 | 38 | public string[] GetTags(Operation operation) => DocumenterSettings.DefaultTags?.ToArray(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Infrastructure/EnricherCoordinator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Infrastructure 6 | { 7 | using Host; 8 | using Interfaces; 9 | using Models; 10 | 11 | /// 12 | /// Class is responsible for coordinating calls between the various enrichers 13 | /// 14 | public class EnricherCoordinator : IApiResourceEnricher 15 | { 16 | private readonly ResourceEnricherManager resourceEnricherManager; 17 | private readonly RequestEnricherManager requestEnricherManager; 18 | 19 | public EnricherCoordinator(IEnrich enricher) 20 | { 21 | resourceEnricherManager = new ResourceEnricherManager(enricher as IResourceEnricher, enricher as IPropertyEnricher); 22 | 23 | var actionEnricherManager = new ActionEnricherManager(enricher as IActionEnricher, enricher as ISecurityEnricher); 24 | requestEnricherManager = new RequestEnricherManager(enricher as IRequestEnricher, actionEnricherManager, resourceEnricherManager.EnrichResource); 25 | } 26 | 27 | /// 28 | /// Enrich supplied ApiResourceDocumentation object with details from Operation object 29 | /// 30 | /// The object to be enriched 31 | /// Details of operation to use for enrichment 32 | public void Enrich(ApiResourceDocumentation resourceSpecification, Operation operation) 33 | { 34 | resourceEnricherManager.EnrichResource(resourceSpecification, operation); 35 | requestEnricherManager.EnrichRequest(resourceSpecification, operation); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Infrastructure/IActionEnricherManager.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Infrastructure 6 | { 7 | using Host; 8 | using Models; 9 | 10 | public interface IActionEnricherManager 11 | { 12 | ApiAction[] EnrichActions(ApiAction[] actions, Operation operation); 13 | } 14 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Infrastructure/RequestEnricherManager.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Infrastructure 6 | { 7 | using System; 8 | using Extensions; 9 | using Host; 10 | using Interfaces; 11 | using Models; 12 | 13 | /// 14 | /// Manages default logic for enriching request DTO objects 15 | /// 16 | public class RequestEnricherManager 17 | { 18 | private readonly IRequestEnricher requestEnricher; 19 | private readonly IActionEnricherManager actionEnricherManager; 20 | private readonly Action enrichResource; 21 | 22 | public RequestEnricherManager(IRequestEnricher requestEnricher, IActionEnricherManager actionEnricherManager, 23 | Action enrichResource) 24 | { 25 | this.requestEnricher = requestEnricher; 26 | this.enrichResource = enrichResource; 27 | 28 | this.actionEnricherManager = actionEnricherManager; 29 | } 30 | 31 | /// 32 | /// Enrich supplied IApiRequest object with details in operation 33 | /// 34 | /// The object to be enriched 35 | /// Details of operation to use for enrichment 36 | public void EnrichRequest(IApiRequest request, Operation operation) 37 | { 38 | if (requestEnricher != null) 39 | { 40 | request.Category = request.Category.GetIfNullOrEmpty(() => requestEnricher.GetCategory(operation)); 41 | request.Tags = request.Tags.GetBasedOnStrategy(() => requestEnricher.GetTags(operation)); 42 | request.AllowMultiple = true; 43 | 44 | request.HasValidator = request.HasValidator.GetIfNoValue(() => requestEnricher.GetHasValidator(operation.RequestType)); 45 | } 46 | 47 | request.ReturnType = 48 | request.ReturnType.GetIfNull(() => CreateApiResourceReturnType(operation)); 49 | 50 | enrichResource(request.ReturnType, operation); 51 | request.Actions = actionEnricherManager.EnrichActions(request.Actions, operation); 52 | } 53 | 54 | private static ApiResourceType CreateApiResourceReturnType(Operation operation) 55 | => operation.ResponseType == null ? null : ApiResourceType.Create(operation.ResponseType); 56 | } 57 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Infrastructure/ResourceEnricherManager.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Infrastructure 6 | { 7 | using Extensions; 8 | using Host; 9 | using Interfaces; 10 | using Models; 11 | 12 | /// 13 | /// Manages default logic for enriching resources 14 | /// 15 | public class ResourceEnricherManager 16 | { 17 | private readonly IResourceEnricher resourceEnricher; 18 | private readonly PropertyEnricherManager propertyEnricherManager; 19 | 20 | public ResourceEnricherManager(IResourceEnricher resourceEnricher, IPropertyEnricher propertyEnricher) 21 | { 22 | this.resourceEnricher = resourceEnricher; 23 | propertyEnricherManager = new PropertyEnricherManager(propertyEnricher, EnrichResource); 24 | } 25 | 26 | /// 27 | /// Enrich supplied IApiResourceType object with details from Operation object 28 | /// 29 | /// The object to be enriched 30 | /// Details of operation to use for enrichment 31 | public void EnrichResource(IApiResourceType resource, Operation operation) 32 | { 33 | // The object that has ResponseStatus is built up from request object 34 | var resourceModel = GetResourceModel(resource, operation); 35 | 36 | if (resourceModel?.ResourceType == null) return; 37 | 38 | EnrichResource(resource, resourceModel); 39 | } 40 | 41 | private static ResourceModel GetResourceModel(IApiResourceType resource, Operation operation) 42 | => resource is IApiRequest 43 | ? new ResourceModel(operation.RequestType, true) 44 | : new ResourceModel(operation.ResponseType, false); 45 | 46 | private void EnrichResource(IApiResourceType resource, ResourceModel resourceModel) 47 | { 48 | if (resourceEnricher != null) 49 | { 50 | var type = resourceModel.ResourceType; 51 | if (resource.Title == resource.TypeName) 52 | resource.Title = resourceEnricher.GetTitle(type.OriginalType); 53 | 54 | resource.Description = resource.Description.GetIfNullOrEmpty(() => resourceEnricher.GetDescription(type.OriginalType)); 55 | resource.Notes = resource.Notes.GetIfNullOrEmpty(() => resourceEnricher.GetNotes(type.OriginalType)); 56 | if (resourceModel.IsRequest) 57 | { 58 | resource.AllowMultiple = 59 | resource.AllowMultiple.GetIfNoValue( 60 | () => resourceEnricher.GetAllowMultiple(type.OriginalType)); 61 | } 62 | else 63 | { 64 | resource.IsCollection = type.OriginalType.IsCollection(); 65 | } 66 | } 67 | 68 | resource.Properties = propertyEnricherManager.EnrichParameters(resource.Properties, resourceModel); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Infrastructure/ResourceModel.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Infrastructure 6 | { 7 | using System; 8 | using ServiceStack.IntroSpec.Extensions; 9 | using ServiceStack.IntroSpec.Models; 10 | 11 | /// 12 | /// Represents details of resource being processed 13 | /// 14 | public class ResourceModel 15 | { 16 | /// 17 | /// Whether is request or response resource 18 | /// 19 | public bool IsRequest { get; } 20 | 21 | /// 22 | /// ClrType of current resource 23 | /// 24 | public ApiClrType ResourceType { get; } 25 | 26 | public ResourceModel(Type resourceType, bool isRequest) 27 | { 28 | IsRequest = isRequest; 29 | ResourceType = resourceType?.ToApiClrType(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IActionEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using Host; 8 | using Models; 9 | 10 | /// 11 | /// Basic operations for implementing a request action 12 | /// 13 | public interface IActionEnricher : IEnrich 14 | { 15 | string[] GetContentTypes(Operation operation, string verb); 16 | RelativePath[] GetRelativePaths(Operation operation, string verb); 17 | StatusCode[] GetStatusCodes(Operation operation, string verb); 18 | string GetNotes(Operation operation, string verb); 19 | } 20 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IApiResourceEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using Host; 8 | using Models; 9 | 10 | /// 11 | /// Basic operations for implementing a new documentation enricher 12 | /// 13 | public interface IApiResourceEnricher 14 | { 15 | /// 16 | /// Enriches ApiResourceDocumentation with information from specified Operation 17 | /// 18 | /// 19 | /// 20 | void Enrich(ApiResourceDocumentation resourceSpecification, Operation operation); 21 | } 22 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IEnrich.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | /// 8 | /// Marker interface showing that class provides some kind of enriching functionality 9 | /// 10 | public interface IEnrich { } 11 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IPropertyEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using System.Reflection; 8 | using Models; 9 | 10 | /// 11 | /// Methods for populating a resource property 12 | /// 13 | public interface IPropertyEnricher : IEnrich 14 | { 15 | string GetTitle(MemberInfo mi); 16 | string GetDescription(MemberInfo mi); 17 | string GetNotes(MemberInfo mi); 18 | bool? GetAllowMultiple(MemberInfo mi); 19 | string[] GetExternalLinks(MemberInfo mi); 20 | PropertyConstraint GetConstraints(MemberInfo mi); 21 | bool? GetIsRequired(MemberInfo mi); 22 | string GetParamType(MemberInfo mi); 23 | } 24 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IRequestEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using System; 8 | using Host; 9 | 10 | /// 11 | /// Methods for populating a request DTO class 12 | /// 13 | public interface IRequestEnricher : IEnrich 14 | { 15 | string GetCategory(Operation operation); 16 | string[] GetTags(Operation operation); 17 | bool? GetHasValidator(Type requestType); 18 | } 19 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/IResourceEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using System; 8 | 9 | /// 10 | /// Methods for populating a resource object 11 | /// 12 | /// Resource is any object returned (embedded in a DTO or the DTO itself). 13 | public interface IResourceEnricher : IEnrich 14 | { 15 | string GetTitle(Type type); 16 | string GetDescription(Type type); 17 | string GetNotes(Type type); 18 | bool? GetAllowMultiple(Type type); 19 | } 20 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Enrichers/Interfaces/ISecurityEnricher.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Enrichers.Interfaces 6 | { 7 | using Host; 8 | using Models; 9 | 10 | /// 11 | /// Methods for populating security information for an action 12 | /// 13 | public interface ISecurityEnricher : IEnrich 14 | { 15 | ApiSecurity GetSecurity(Operation operation, string verb); 16 | } 17 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/AccessExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System; 8 | using System.Linq; 9 | using DataAnnotations; 10 | using Infrastructure; 11 | 12 | public static class AccessExtensions 13 | { 14 | public static bool CanAccess(this RestrictAttribute restrictAttribute, Result request) 15 | => restrictAttribute == null || (request.IsSuccess && restrictAttribute.HasAccessTo(request.Value)); 16 | 17 | public static bool HasAccessToFeature(this Type requestType, Result feature) 18 | => !feature.IsSuccess || 19 | !requestType.AllAttributes().Any(t => t.Feature.HasFlag(feature.Value)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/ApiDocumentationExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System; 8 | using System.Linq; 9 | using System.Linq.Expressions; 10 | using DTO; 11 | using Linq; 12 | using Models; 13 | 14 | public static class ApiDocumentationExtensions 15 | { 16 | public static ApiDocumentation Filter(this ApiDocumentation documentation, IFilterableSpecRequest request) 17 | { 18 | request.ThrowIfNull(nameof(request)); 19 | documentation.ThrowIfNull(nameof(documentation)); 20 | 21 | var filter = GetFilter(request); 22 | if (filter != null) 23 | documentation = documentation.CreateCopy(filter.Compile()); 24 | return documentation; 25 | } 26 | 27 | public static ApiDocumentation WithBaseUrl(this ApiDocumentation documentation, string appBaseUrl) 28 | { 29 | documentation.ThrowIfNull(nameof(documentation)); 30 | 31 | documentation.ApiBaseUrl = appBaseUrl.ThrowIfNullOrEmpty(nameof(appBaseUrl)); 32 | return documentation; 33 | } 34 | 35 | private static Expression> GetFilter(IFilterableSpecRequest request) 36 | { 37 | bool hasFilter = false; 38 | 39 | // The true predicate is just a starter to use as base 40 | var predicate = PredicateBuilder.True(); 41 | if (!request.DtoNames.IsNullOrEmpty()) 42 | { 43 | hasFilter = true; 44 | predicate = predicate.And(doc => request.DtoNames.Contains(doc.TypeName, StringComparer.OrdinalIgnoreCase)); 45 | } 46 | 47 | if (!request.Tags.IsNullOrEmpty()) 48 | { 49 | hasFilter = true; 50 | predicate = predicate.And(doc => request.Tags.Any(t => doc.Tags.Contains(t, StringComparer.OrdinalIgnoreCase))); 51 | } 52 | 53 | if (!request.Categories.IsNullOrEmpty()) 54 | { 55 | hasFilter = true; 56 | predicate = predicate.And(doc => request.Categories.Contains(doc.Category, StringComparer.OrdinalIgnoreCase)); 57 | } 58 | 59 | return hasFilter ? predicate : null; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using Settings; 11 | 12 | public static class CollectionExtensions 13 | { 14 | public static bool IsNullOrEmpty(this T[] array) => array == null || array.Length == 0; 15 | 16 | public static bool IsNullOrEmpty(this IList array) => array == null || array.Count == 0; 17 | 18 | public static bool IsNullOrEmpty(this IEnumerable array) => array == null || !array.Any(); 19 | 20 | /// 21 | /// Produces the set union of two sequences by using the default equality comparer. 22 | /// Verifies validity of arrays by checking for null before running union 23 | /// 24 | /// Type of array 25 | /// Array whose elemets form first set for the union 26 | /// Function to get array whose elements form second set for the union 27 | /// Array that contains the distinct elements from both input arrays 28 | public static T[] SafeUnion(this T[] array, Func getValues) 29 | { 30 | if (array.IsNullOrEmpty()) 31 | return getValues(); 32 | 33 | IEnumerable enumerable = getValues(); 34 | if (enumerable.IsNullOrEmpty()) 35 | return array; 36 | 37 | return array.Union(enumerable).ToArray(); 38 | } 39 | 40 | /// 41 | /// Produces the set union of two sequences by using the default equality comparer. 42 | /// Verifies validity of enumerables by checking for null before running union 43 | /// 44 | /// Type of enumerable 45 | /// Enumerable whose elemets form first set for the union 46 | /// Enumerable whose elemets form second set for the union 47 | /// Array that contains the distinct elements from both input arrays 48 | public static IEnumerable SafeUnion(this IEnumerable array, IEnumerable enumerable) 49 | { 50 | if (array.IsNullOrEmpty()) 51 | return enumerable; 52 | 53 | if (enumerable.IsNullOrEmpty()) 54 | return array; 55 | 56 | return array.Union(enumerable).ToArray(); 57 | } 58 | 59 | /// 60 | /// Populates array based on current DocumenterSettings.CollectionStrategy 61 | /// 62 | /// Type of array 63 | /// Array to get values for 64 | /// Function to get array 65 | /// New array, construction of which is driven by DocumenterSettings.CollectionStrategy 66 | public static T[] GetBasedOnStrategy(this T[] array, Func getValues) 67 | { 68 | bool unionCollections = DocumenterSettings.CollectionStrategy == EnrichmentStrategy.Union; 69 | 70 | return unionCollections ? array.SafeUnion(getValues) : array.GetIfNullOrEmpty(getValues); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/GetValueExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System; 8 | 9 | /// 10 | /// Collection of extension methods used for calling Func to get value if value is empty/default 11 | /// 12 | public static class GetValueExtensions 13 | { 14 | public static string GetIfNullOrEmpty(this string value, Func getValue) 15 | => string.IsNullOrEmpty(value) ? getValue() : value; 16 | 17 | public static T GetIfNull(this T value, Func getValue) 18 | where T : class 19 | => value ?? getValue(); 20 | 21 | public static T[] GetIfNullOrEmpty(this T[] array, Func getValue) 22 | => array.IsNullOrEmpty() ? getValue() : array; 23 | 24 | public static T? GetIfNoValue(this T? value, Func getValue) 25 | where T : struct 26 | => value ?? getValue(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/OperationExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using Host; 11 | using Logging; 12 | using Models; 13 | using Utilities; 14 | using Web; 15 | 16 | public static class OperationExtensions 17 | { 18 | private static readonly ILog log = LogManager.GetLogger(typeof(OperationExtensions)); 19 | 20 | public static bool IsForVerb(this RouteAttribute routeAttribute, string verb) 21 | { 22 | if (routeAttribute == null) 23 | return false; 24 | 25 | if (string.IsNullOrEmpty(routeAttribute.Verbs)) 26 | return true; 27 | 28 | return routeAttribute.Verbs.IndexOf(verb, StringComparison.OrdinalIgnoreCase) >= 0; 29 | } 30 | 31 | public static bool IsForVerb(this AuthenticateAttribute authAttribute, string verb) 32 | { 33 | if (authAttribute == null) 34 | return false; 35 | 36 | if (authAttribute.ApplyTo == ApplyTo.All) 37 | return true; 38 | 39 | var applyTo = EnumUtilities.SafeParse(verb); 40 | 41 | if (!applyTo.IsSuccess) 42 | log.Info($"Unable to parse verb {verb} to ApplyTo enum"); 43 | 44 | return applyTo.IsSuccess && authAttribute.ApplyTo.HasFlag(applyTo.Value); 45 | } 46 | 47 | public static bool AuthenticationAppliesForVerb(this Operation operation, string verb) 48 | { 49 | var authenticateAttr = operation.GetRequestAttr(); 50 | 51 | // NOTE AuthenticateAttribute is AllowMultiple = false so be 0:1 52 | return authenticateAttr.IsNullOrEmpty() || authenticateAttr.FirstOrDefault().IsForVerb(verb); 53 | } 54 | 55 | public static Permissions GetRoles(this Operation operation, string verb) 56 | { 57 | var any = operation.GetRoles(verb, attribute => attribute.RequiredRoles); 58 | var all = operation.GetRoles(verb, attribute => attribute.RequiredRoles); 59 | 60 | return Permissions.Create(any, all); 61 | } 62 | 63 | public static Permissions GetPermissions(this Operation operation, string verb) 64 | { 65 | var any = operation.GetRoles(verb, attribute => attribute.RequiredPermissions); 66 | var all = operation.GetRoles(verb, attribute => attribute.RequiredPermissions); 67 | 68 | return Permissions.Create(any, all); 69 | } 70 | 71 | private static IEnumerable GetRequestAttr(this Operation operation) where T : IRequestFilterBase 72 | => operation.RequestFilterAttributes?.OfType(); 73 | 74 | private static List GetRoles(this Operation operation, string verb, 75 | Func> func) where T : AuthenticateAttribute 76 | => operation.GetRequestAttr()? 77 | .Where(a => a.IsForVerb(verb)) 78 | .SelectMany(func) 79 | .ToList(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System.Text; 8 | 9 | public static class StringExtensions 10 | { 11 | private const char Space = ' '; 12 | 13 | /// 14 | /// Takes a camel/pascal-case string and adds space before any Capital letter (except first) 15 | /// 16 | /// String to operate on 17 | /// String split on capitals 18 | /// Based on accepted answer for http://stackoverflow.com/questions/272633/add-spaces-before-capital-letters 19 | public static string ToSpaced(this string text) 20 | { 21 | if (string.IsNullOrWhiteSpace(text)) 22 | return string.Empty; 23 | 24 | var newText = new StringBuilder(text.Length * 2); 25 | newText.Append(text[0]); 26 | 27 | for (var x = 1; x < text.Length; x++) 28 | { 29 | if (char.IsUpper(text[x]) && (IsNewUpper(text, x) || IsEndOfAcronym(text, x))) 30 | newText.Append(Space); 31 | newText.Append(text[x]); 32 | } 33 | return newText.ToString(); 34 | } 35 | 36 | /// 37 | /// Removes occurrence of toTrim from start of string 38 | /// 39 | /// Text to trim 40 | /// String value to trim 41 | /// Trimmed string 42 | public static string TrimStart(this string text, string toTrim) 43 | { 44 | if (string.IsNullOrEmpty(text) || !text.StartsWith(toTrim)) 45 | return text; 46 | 47 | return text.Substring(toTrim.Length); 48 | } 49 | 50 | /// 51 | /// Ensures that the provided string ends with endWith string. Will be added if does not currently end with. 52 | /// 53 | /// Text to end with 54 | /// String to add at end of string 55 | /// String ended in required string 56 | public static string EnsureEndsWith(this string text, string endWith) 57 | { 58 | if (string.IsNullOrEmpty(text) || text.EndsWith(endWith)) 59 | return text; 60 | 61 | return $"{text}{endWith}"; 62 | } 63 | 64 | private static bool IsEndOfAcronym(string text, int index) 65 | { 66 | // Preceding char is upper AND next character isn't beyond bounds AND next char isn't upper 67 | return char.IsUpper(text[index - 1]) && index < text.Length - 1 && !char.IsUpper(text[index + 1]); 68 | } 69 | 70 | private static bool IsNewUpper(string text, int index) 71 | { 72 | // Preceding char is not a space OR uppercase 73 | var preceding = text[index - 1]; 74 | return !(preceding == Space || char.IsUpper(preceding)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Extensions/UrlExtensions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Extensions 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text.RegularExpressions; 10 | 11 | public static class UrlExtensions 12 | { 13 | // Regex to get any 14 | private static readonly Regex pathVariableRegex = new Regex("\\{([A-Za-z0-9-_]+)\\}"); 15 | 16 | // TODO Handle wildcards 17 | public static List GetPathParams(this string path) 18 | { 19 | if (path.IsNullOrEmpty()) 20 | return Enumerable.Empty().ToList(); 21 | 22 | var matches = pathVariableRegex.Matches(path); 23 | 24 | var output = new List(); 25 | foreach (Match match in matches) 26 | { 27 | if (!match.Success) 28 | continue; 29 | 30 | output.Add(match.Groups[1].Value); 31 | } 32 | 33 | return output.Count > 0 ? output : Enumerable.Empty().ToList(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/HttpVerbs.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | namespace ServiceStack.IntroSpec 5 | { 6 | using System; 7 | 8 | [Flags] 9 | public enum HttpVerbs 10 | { 11 | Get = 1, 12 | Post = 2, 13 | Put = 4, 14 | Delete = 8, 15 | Head = 16, // 0x00000010 16 | Patch = 32, // 0x00000020 17 | Options = 64, // 0x00000040 18 | } 19 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/IApiDocumentationGenerator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | using System.Collections.Generic; 8 | using Host; 9 | using Models; 10 | 11 | /// 12 | /// Basic signature used to generate documentation POCO 13 | /// 14 | public interface IApiDocumentationGenerator 15 | { 16 | /// 17 | /// Generate documentation object from registered service operations 18 | /// 19 | /// List of metadata operations 20 | /// Running IAppHost 21 | /// The main ApiSpec settings class 22 | /// 23 | ApiDocumentation GenerateDocumentation(IEnumerable operations, IAppHost appHost, 24 | IApiSpecSettings settings); 25 | } 26 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/IApiSpecSettings.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec 6 | { 7 | using System; 8 | 9 | public interface IApiSpecSettings 10 | { 11 | string ContactEmail { get; set; } 12 | string ContactName { get; set; } 13 | Uri ContactUrl { get; set; } 14 | string Description { get; set; } 15 | Uri LicenseUrl { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Infrastructure/Result.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Infrastructure 6 | { 7 | public class Result 8 | { 9 | public bool IsSuccess { get; } 10 | public T Value { get; } 11 | 12 | protected Result(T value, bool isSuccess) 13 | { 14 | IsSuccess = isSuccess; 15 | Value = value; 16 | } 17 | 18 | public static Result Fail() 19 | { 20 | return new Result(default(T), false); 21 | } 22 | 23 | public static Result Fail(T value) 24 | { 25 | return new Result(value, false); 26 | } 27 | 28 | public static Result Success(T value) 29 | { 30 | return new Result(value, true); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Linq/PredicateBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceStack.IntroSpec.Linq 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | /// PredicateBuilder class from LINQKit by Joseph + Ben Albahari 7 | /// http://www.albahari.com/nutshell/predicatebuilder.aspx 8 | public static class PredicateBuilder 9 | { 10 | public static Expression> True() { return f => true; } 11 | public static Expression> False() { return f => false; } 12 | 13 | public static Expression> Or(this Expression> expr1, 14 | Expression> expr2) 15 | { 16 | var invokedExpr = Expression.Invoke(expr2, expr1.Parameters); 17 | return Expression.Lambda> 18 | (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters); 19 | } 20 | 21 | public static Expression> And(this Expression> expr1, 22 | Expression> expr2) 23 | { 24 | var invokedExpr = Expression.Invoke(expr2, expr1.Parameters); 25 | return Expression.Lambda> 26 | (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiAction.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public class ApiAction : IApiAction 8 | { 9 | public ApiSecurity Security { get; set; } 10 | public string Verb { get; set; } 11 | public string Notes { get; set; } 12 | public StatusCode[] StatusCodes { get; set; } 13 | public string[] ContentTypes { get; set; } 14 | public RelativePath[] RelativePaths { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiClrType.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceStack.IntroSpec.Models 2 | { 3 | using System; 4 | using System.Runtime.Serialization; 5 | 6 | public class ApiClrType 7 | { 8 | public string Name { get; set; } 9 | public string AssemblyName { get; set; } 10 | public bool IsPrimitive { get; set; } 11 | [IgnoreDataMember] 12 | public Type OriginalType { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiContact.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | /// 8 | /// Technical contact details for this api 9 | /// 10 | public class ApiContact 11 | { 12 | public string Name { get; set; } 13 | public string Url { get; set; } 14 | public string Email { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiDocumentation.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System; 8 | using System.Linq; 9 | 10 | /// 11 | /// General top level model with API (Service) wide vars 12 | /// 13 | public class ApiDocumentation 14 | { 15 | public string Title { get; set; } 16 | 17 | public string ApiVersion { get; set; } 18 | 19 | public string ApiBaseUrl { get; set; } 20 | 21 | public string Description { get; set; } 22 | 23 | public string TermsOfService { get; set; } 24 | 25 | public string Licence { get; set; } 26 | 27 | public string LicenceUrl { get; set; } 28 | 29 | public ApiContact Contact { get; set; } 30 | 31 | public ApiResourceDocumentation[] Resources { get; set; } 32 | 33 | public ApiPlugin[] Plugins { get; set; } 34 | 35 | public ApiDocumentation CreateCopy(Func resourcesFilter) 36 | { 37 | var apiDocumentation = MemberwiseClone() as ApiDocumentation; 38 | apiDocumentation.Resources = Resources.Where(resourcesFilter).ToArray(); 39 | return apiDocumentation; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiPlugin.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public class ApiPlugin 8 | { 9 | public string Name { get; set; } 10 | 11 | public string Version { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiPropertyDocumentation.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System; 8 | 9 | /// 10 | /// Each request property within a DTO 11 | /// 12 | public class ApiPropertyDocumentation : IApiSpec 13 | { 14 | // Set when instantiated 15 | public string Id { get; set; } 16 | public ApiClrType ClrType { get; set; } 17 | 18 | // From IApiSpec 19 | private string title; 20 | public string Title 21 | { 22 | get { return title ?? Id; } 23 | set 24 | { 25 | if (!string.IsNullOrEmpty(value)) 26 | title = value; 27 | } 28 | } 29 | 30 | public string Description { get; set; } 31 | public string Notes { get; set; } 32 | 33 | public string ParamType { get; set; } // used to denote if the param is request querystring or body restricted 34 | public bool? IsRequired { get; set; } 35 | public string[] ExternalLinks { get; set; } 36 | public bool? AllowMultiple { get; set; } 37 | public bool? IsCollection { get; set; } 38 | 39 | public IApiResourceType EmbeddedResource { get; set; } 40 | 41 | public PropertyConstraint Constraints { get; set; } 42 | } 43 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiResourceDocumentation.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System.Diagnostics; 8 | 9 | /// 10 | /// A single API resource (DTO) 11 | /// 12 | [DebuggerDisplay("{Title}")] 13 | public class ApiResourceDocumentation : IApiResourceType, IApiRequest 14 | { 15 | // Set when instantiating 16 | public string TypeName { get; set; } 17 | 18 | // From IApiResourceType 19 | private string title; 20 | public string Title 21 | { 22 | get { return title ?? TypeName; } 23 | set 24 | { 25 | if (!string.IsNullOrEmpty(value)) 26 | title = value; 27 | } 28 | } 29 | 30 | public string Description { get; set; } 31 | public string Notes { get; set; } 32 | public ApiPropertyDocumentation[] Properties { get; set; } 33 | public bool? AllowMultiple { get; set; } 34 | public bool? HasValidator { get; set; } 35 | public bool? IsCollection { get; set; } 36 | 37 | // From IApiRequest 38 | public ApiAction[] Actions { get; set; } 39 | public ApiResourceType ReturnType { get; set; } 40 | 41 | // From IApiMetadata by way of IApiRequest 42 | public string Category { get; set; } 43 | public string[] Tags { get; set; } 44 | } 45 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiResourceType.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System; 8 | 9 | using ServiceStack.IntroSpec.Extensions; 10 | 11 | public class ApiResourceType : IApiResourceType 12 | { 13 | private string title; 14 | public string Title 15 | { 16 | get { return title ?? TypeName; } 17 | set 18 | { 19 | if (!string.IsNullOrEmpty(value)) 20 | title = value; 21 | } 22 | } 23 | public string Description { get; set; } 24 | public string Notes { get; set; } 25 | public string TypeName { get; set; } 26 | public ApiPropertyDocumentation[] Properties { get; set; } 27 | public bool? AllowMultiple { get; set; } 28 | 29 | public bool? IsCollection { get; set; } 30 | 31 | public static ApiResourceType Create(Type resourceType) 32 | => 33 | new ApiResourceType 34 | { 35 | TypeName = resourceType.GetDocumentationTypeName(), 36 | IsCollection = resourceType.IsCollection() 37 | }; 38 | } 39 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ApiSecurity.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public class ApiSecurity 8 | { 9 | public bool IsProtected { get; set; } 10 | public Permissions Roles { get; set; } 11 | public Permissions Permissions { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/IApiAction.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface IApiAction : ISecured 8 | { 9 | string[] ContentTypes { get; set; } 10 | RelativePath[] RelativePaths { get; set; } 11 | StatusCode[] StatusCodes { get; set; } 12 | string Verb { get; set; } 13 | string Notes { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/IApiMetadata.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface IApiMetadata 8 | { 9 | string Category { get; set; } 10 | string[] Tags { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/IApiRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface IApiRequest : IApiMetadata 8 | { 9 | ApiResourceType ReturnType { get; set; } 10 | ApiAction[] Actions { get; set; } 11 | bool? AllowMultiple { get; set; } 12 | bool? HasValidator { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/IApiResourceType.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface IApiResourceType : IApiSpec 8 | { 9 | string TypeName { get; set; } 10 | ApiPropertyDocumentation[] Properties { get; set; } 11 | bool? AllowMultiple { get; set; } 12 | bool? IsCollection { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/IApiSpec.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface IApiSpec 8 | { 9 | string Title { get; set; } 10 | string Description { get; set; } 11 | string Notes { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/ISecured.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public interface ISecured 8 | { 9 | ApiSecurity Security { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/Permissions.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System.Collections.Generic; 8 | using Extensions; 9 | 10 | /// 11 | /// Represents a group of permission 12 | /// 13 | public class Permissions 14 | { 15 | /// 16 | /// Specifies that Any of these permissions are required for access 17 | /// 18 | public string[] AnyOf { get; set; } 19 | 20 | /// 21 | /// A list of permissions, all of which must be present for access 22 | /// 23 | public string[] AllOf { get; set; } 24 | 25 | public static Permissions Create(IList anyOf, IList allOf) 26 | { 27 | var any = anyOf.IsNullOrEmpty() ? null : anyOf.ToArray(); 28 | var all = allOf.IsNullOrEmpty() ? null : allOf.ToArray(); 29 | 30 | if (any == null && all == null) 31 | return null; 32 | 33 | return new Permissions { AllOf = all, AnyOf = any }; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/PropertyConstraint.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System; 8 | 9 | public class PropertyConstraint 10 | { 11 | public string Name { get; set; } 12 | public int? Min { get; set; } 13 | public int? Max { get; set; } 14 | public string[] Values { get; set; } 15 | public ConstraintType Type { get; set; } 16 | 17 | public static PropertyConstraint RangeConstraint(string name, int? min, int? max) 18 | { 19 | if (!min.HasValue && !max.HasValue) 20 | throw new InvalidOperationException("You must supply either a Min or Max value"); 21 | 22 | if ((min ?? int.MinValue) > (max ?? int.MaxValue)) 23 | throw new ArgumentOutOfRangeException(nameof(max), "Min cannot be greater than Max"); 24 | 25 | return new PropertyConstraint { Name = name, Min = min, Max = max, Type = ConstraintType.Range }; 26 | } 27 | 28 | public static PropertyConstraint ListConstraint(string name, string[] values) 29 | { 30 | if ((values == null) || (values.Length == 0)) 31 | throw new ArgumentException("You must supply a list of values", nameof(values)); 32 | 33 | return new PropertyConstraint { Name = name, Values = values, Type = ConstraintType.List }; 34 | } 35 | } 36 | 37 | public enum ConstraintType 38 | { 39 | Range, 40 | List 41 | } 42 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/RelativePath.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | public class RelativePath 8 | { 9 | public string Path { get; set; } 10 | public string Source { get; set; } 11 | 12 | public static implicit operator RelativePath(string path) 13 | => new RelativePath { Path = path }; 14 | 15 | public static implicit operator string(RelativePath path) 16 | => path?.Path; 17 | 18 | public bool IsAutoRoute => Source == Constants.RouteSources.AutoRoute; 19 | public bool IsFallback => Source == Constants.RouteSources.FallbackRoute; 20 | } 21 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Models/StatusCode.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Models 6 | { 7 | using System; 8 | using System.Net; 9 | using Extensions; 10 | 11 | public class StatusCode : IEquatable 12 | { 13 | public int Code { get; set; } 14 | public string Description { get; set; } 15 | public string Name { get; set; } 16 | 17 | public StatusCode WithDescription(string description) 18 | { 19 | Description = description; 20 | return this; 21 | } 22 | 23 | public StatusCode WithCode(int code) 24 | { 25 | Code = code; 26 | return this; 27 | } 28 | 29 | public StatusCode WithName(string name) 30 | { 31 | Name = name; 32 | return this; 33 | } 34 | 35 | public static explicit operator StatusCode(int statusCode) 36 | { 37 | var httpStatusCode = (HttpStatusCode) statusCode; 38 | return (StatusCode) httpStatusCode; 39 | } 40 | 41 | public static explicit operator StatusCode(HttpStatusCode httpStatusCode) 42 | { 43 | return new StatusCode { Code = (int) httpStatusCode, Name = httpStatusCode.ToString().ToSpaced() }; 44 | } 45 | 46 | public bool Equals(StatusCode other) 47 | { 48 | if (ReferenceEquals(null, other)) return false; 49 | if (ReferenceEquals(this, other)) return true; 50 | return Code == other.Code; 51 | } 52 | 53 | public override bool Equals(object obj) 54 | { 55 | if (ReferenceEquals(null, obj)) return false; 56 | if (ReferenceEquals(this, obj)) return true; 57 | if (obj.GetType() != GetType()) return false; 58 | return Equals((StatusCode) obj); 59 | } 60 | 61 | public override int GetHashCode() 62 | { 63 | return Code; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/DTO/PostmanRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.DTO 6 | { 7 | using System.Runtime.Serialization; 8 | using DataAnnotations; 9 | using IntroSpec.DTO; 10 | using Models; 11 | 12 | [Route(Constants.PostmanSpecUri)] 13 | [Exclude(Feature.Metadata | Feature.ServiceDiscovery)] 14 | [DataContract] 15 | public class PostmanRequest : IReturn, IFilterableSpecRequest 16 | { 17 | [DataMember(Name = "DtoName")] 18 | public string[] DtoNames { get; set; } 19 | 20 | [DataMember(Name = "Category")] 21 | public string[] Categories { get; set; } 22 | 23 | [DataMember(Name = "Tag")] 24 | public string[] Tags { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/DTO/PostmanResponse.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.DTO 6 | { 7 | using Models; 8 | 9 | public class PostmanResponse 10 | { 11 | public PostmanSpecCollection Collection { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Models/Postman.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Models 6 | { 7 | using DataAnnotations; 8 | 9 | // https://github.com/postmanlabs/schemas/blob/develop/json/collection/v2.0.0/index.json 10 | [Exclude(Feature.Soap | Feature.ServiceDiscovery)] 11 | public class Postman 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Models/PostmanFolder.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Models 6 | { 7 | using System.Collections.Generic; 8 | using System.Runtime.Serialization; 9 | 10 | [DataContract] 11 | public class PostmanFolder 12 | { 13 | [DataMember(Name = "id")] 14 | public string Id { get; set; } 15 | 16 | [DataMember(Name = "name")] 17 | public string Name { get; set; } 18 | 19 | [DataMember(Name = "description")] 20 | public string Description { get; set; } 21 | 22 | [DataMember(Name = "order")] 23 | public List RequestIds { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Models/PostmanSpecCollection.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Models 6 | { 7 | using System.Collections.Generic; 8 | using System.Runtime.Serialization; 9 | 10 | [DataContract] 11 | public class PostmanSpecCollection 12 | { 13 | [DataMember(Name = "id")] 14 | public string Id { get; set; } 15 | 16 | [DataMember(Name = "name")] 17 | public string Name { get; set; } 18 | 19 | [DataMember(Name = "description")] 20 | public string Description { get; set; } 21 | 22 | // NOTE If order not specified in collection folders are ignored 23 | [DataMember(Name = "order")] 24 | public string[] Order { get; set; } = new string[0]; 25 | 26 | [DataMember(Name = "folders")] 27 | public List Folders { get; set; } = new List(); 28 | 29 | [DataMember(Name = "timestamp")] 30 | public long Timestamp { get; set; } 31 | 32 | // owner 33 | // remoteLink 34 | 35 | [DataMember(Name = "public")] 36 | public bool Public { get; set; } 37 | 38 | [DataMember(Name = "requests")] 39 | public PostmanSpecRequest[] Requests { get; set; } 40 | } 41 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Models/PostmanSpecData.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Models 6 | { 7 | using System.Runtime.Serialization; 8 | 9 | [DataContract] 10 | public class PostmanSpecData 11 | { 12 | [DataMember(Name = "key")] 13 | public string Key { get; set; } 14 | 15 | [DataMember(Name = "value")] 16 | public string Value { get; set; } 17 | 18 | [DataMember(Name = "type")] 19 | public string Type { get; set; } 20 | 21 | [DataMember(Name = "enabled")] 22 | public bool Enabled { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Models/PostmanSpecRequest.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Models 6 | { 7 | using System.Collections.Generic; 8 | using System.Runtime.Serialization; 9 | 10 | [DataContract] 11 | public class PostmanSpecRequest 12 | { 13 | [DataMember(Name = "id")] 14 | public string Id { get; set; } 15 | 16 | [DataMember(Name = "headers")] 17 | public string Headers { get; set; } 18 | 19 | [DataMember(Name = "url")] 20 | public string Url { get; set; } 21 | 22 | // preRequestScript 23 | 24 | [DataMember(Name = "pathVariables")] 25 | public Dictionary PathVariables { get; set; } 26 | 27 | [DataMember(Name = "method")] 28 | public string Method { get; set; } 29 | 30 | [DataMember(Name = "data")] 31 | public List Data { get; set; } 32 | 33 | [DataMember(Name = "dataMode")] 34 | public string DataMode { get; set; } = "params"; // raw? 35 | 36 | [DataMember(Name = "version")] 37 | public int Version { get; set; } = 2; 38 | 39 | // tests 40 | // currentHelper 41 | 42 | [DataMember(Name = "time")] 43 | public long Time { get; set; } 44 | 45 | [DataMember(Name = "name")] 46 | public string Name { get; set; } 47 | 48 | [DataMember(Name = "description")] 49 | public string Description { get; set; } 50 | 51 | // descriptionFormat 52 | 53 | [DataMember(Name = "collectionId")] 54 | public string CollectionId { get; set; } 55 | 56 | [DataMember(Name = "responses")] 57 | public string[] Responses { get; set; } = new string[0]; 58 | 59 | [DataMember(Name = "folder")] 60 | public string FolderId { get; set; } 61 | 62 | // tests 63 | 64 | // rawModeData 65 | 66 | // helperAttributes 67 | } 68 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Postman/Services/ApiSpecPostmanService.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Postman.Services 6 | { 7 | using DTO; 8 | using Extensions; 9 | using IntroSpec.Services; 10 | 11 | #if !DEBUG 12 | [CacheResponse(MaxAge = 300, Duration = 600)] 13 | #endif 14 | public class ApiSpecPostmanService : Service 15 | { 16 | private readonly IApiDocumentationProvider documentationProvider; 17 | 18 | public ApiSpecPostmanService(IApiDocumentationProvider documentationProvider) 19 | { 20 | this.documentationProvider = documentationProvider.ThrowIfNull(nameof(documentationProvider)); 21 | } 22 | 23 | [AddHeader(ContentType = MimeTypes.Json)] 24 | public object Get(PostmanRequest request) 25 | { 26 | // Get the filtered documentation object 27 | var documentation = documentationProvider.GetApiDocumentation(Request.GetApplicationUrl()).Filter(request); 28 | 29 | var postmanGenerator = new PostmanCollectionGenerator(); 30 | var collection = postmanGenerator.Generate(documentation); 31 | return collection; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/ServiceStack.IntroSpec.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net452;netstandard2.0 4 | true 5 | 0.1.0 6 | ServiceStack plugin that uses introspection to generate documentation about services in AppHost 7 | Copyright © 2016 - Present 8 | servicestack microservices introspection documentation spec specification api postman 9 | https://github.com/wwwlicious/servicestack-introspec 10 | https://github.com/wwwlicious/servicestack-introspec 11 | https://opensource.org/licenses/MPL-2.0 12 | https://github.com/wwwlicious/ServiceStack-IntroSpec/master/assets/logo_notext.png 13 | Donald Gray (@donaldgray); Scott Mackay (@wwwlicious) 14 | https://github.com/wwwlicious/servicestack-introspec 15 | true 16 | 17 | 18 | NETSTANDARD2_0; 19 | 20 | 21 | NET452; 22 | 23 | 24 | bin\Release\netstandard2.0\ServiceStack.Introspec.xml 25 | full 26 | true 27 | false 28 | 29 | 30 | bin\Release\net452\ServiceStack.Introspec.xml 31 | full 32 | true 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/ServiceStack.IntroSpec.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ServiceStack.IntroSpec 5 | $version$ 6 | Donald Gray (@donaldgray); Scott Mackay (@wwwlicious) 7 | ServiceStack plugin that uses introspection to generate documentation about services in AppHost 8 | https://github.com/wwwlicious/ServiceStack-IntroSpec 9 | https://opensource.org/licenses/MPL-2.0 10 | https://github.com/wwwlicious/ServiceStack-IntroSpec/master/assets/logo_notext.png 11 | false 12 | servicestack microservices introspection documentation spec specification api postman 13 | See github project for details 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Services/ApiDocumentationProvider.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Services 6 | { 7 | using Models; 8 | using ServiceStack.IntroSpec.Extensions; 9 | 10 | public class ApiDocumentationProvider : IApiDocumentationProvider 11 | { 12 | public ApiDocumentation GetApiDocumentation(string appBaseUrl) 13 | { 14 | appBaseUrl.ThrowIfNullOrEmpty(nameof(appBaseUrl)); 15 | 16 | var apiSpecFeature = HostContext.GetPlugin(); 17 | return apiSpecFeature.Documentation.WithBaseUrl(appBaseUrl); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Services/ApiSpecMetadataService.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Services 6 | { 7 | using DTO; 8 | using Utilities; 9 | 10 | #if !DEBUG 11 | [CacheResponse(MaxAge = 300, Duration = 600)] 12 | #endif 13 | public class ApiSpecMetadataService : Service 14 | { 15 | private readonly IApiDocumentationProvider documentationProvider; 16 | 17 | public ApiSpecMetadataService(IApiDocumentationProvider documentationProvider) 18 | { 19 | this.documentationProvider = documentationProvider.ThrowIfNull(nameof(documentationProvider)); 20 | } 21 | 22 | public object Get(SpecMetadataRequest request) 23 | { 24 | var documentation = documentationProvider.GetApiDocumentation(Request.GetApplicationUrl()); 25 | return ApiSpecMetadataUtilities.GenerateResponse(documentation); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Services/ApiSpecService.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Services 6 | { 7 | using System; 8 | 9 | using DTO; 10 | using Extensions; 11 | 12 | using ServiceStack.Text; 13 | 14 | #if !DEBUG 15 | [CacheResponse(MaxAge = 300, Duration = 600)] 16 | #endif 17 | public class ApiSpecService : Service 18 | { 19 | private readonly IApiDocumentationProvider documentationProvider; 20 | 21 | public ApiSpecService(IApiDocumentationProvider documentationProvider) 22 | { 23 | this.documentationProvider = documentationProvider.ThrowIfNull(nameof(documentationProvider)); 24 | } 25 | 26 | public object Get(SpecRequest request) 27 | { 28 | // Get the filtered documentation to return 29 | var documentation = documentationProvider.GetApiDocumentation(Request.GetApplicationUrl()).Filter(request); 30 | using (JsConfig.BeginScope()) 31 | { 32 | // intercept and output any Type's formatted for documentation 33 | JsConfig.SerializeFn = x => x.GetDocumentationTypeName(); 34 | return new SpecResponse { ApiDocumentation = documentation }; 35 | } 36 | 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Services/IApiDocumentationProvider.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Services 6 | { 7 | using Models; 8 | 9 | public interface IApiDocumentationProvider 10 | { 11 | ApiDocumentation GetApiDocumentation(string appBaseUrl); 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Settings/ApiSpecConfig.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Settings 6 | { 7 | using System; 8 | using Models; 9 | using TypeSpec; 10 | 11 | [Obsolete("Use public properties of ApiSpecFeature")] 12 | public class ApiSpecConfig : IFluentInterface 13 | { 14 | [Obsolete("Use Description property of ApiSpecFeature")] 15 | public string Description { get; set; } 16 | 17 | [Obsolete("Use ContactUrl property of ApiSpecFeature")] 18 | public Uri LicenseUrl { get; set; } 19 | 20 | private ApiContact contact; 21 | [Obsolete("Use ContactEmail/ContactName/ContactUrl properties of ApiSpecFeature")] 22 | public ApiContact Contact 23 | { 24 | get { return contact ?? (contact = new ApiContact()); } 25 | set { contact = value; } 26 | } 27 | 28 | [Obsolete("Use Description property of ApiSpecFeature")] 29 | public ApiSpecConfig WithDescription(string description) 30 | { 31 | Description = description; 32 | return this; 33 | } 34 | 35 | [Obsolete("Use LicenseUrl property of ApiSpecFeature")] 36 | public ApiSpecConfig WithLicenseUrl(Uri licenseUrl) 37 | { 38 | LicenseUrl = licenseUrl; 39 | return this; 40 | } 41 | 42 | [Obsolete("Use ContactEmail/ContactName/ContactUrl properties of ApiSpecFeature")] 43 | public ApiSpecConfig WithContact(ApiContact apiContact) 44 | { 45 | Contact = apiContact; 46 | return this; 47 | } 48 | 49 | [Obsolete("Use ContactName property of ApiSpecFeature")] 50 | public ApiSpecConfig WithContactName(string name) 51 | { 52 | Contact.Name = name; 53 | return this; 54 | } 55 | 56 | [Obsolete("Use ContactUrl property of ApiSpecFeature")] 57 | public ApiSpecConfig WithContactUrl(Uri url) 58 | { 59 | Contact.Url = url.OriginalString; 60 | return this; 61 | } 62 | 63 | [Obsolete("Use ContactEmail property of ApiSpecFeature")] 64 | public ApiSpecConfig WithContactEmail(string email) 65 | { 66 | Contact.Email = email; 67 | return this; 68 | } 69 | 70 | internal void PopulateProperties(IApiSpecSettings feature) 71 | { 72 | if (!string.IsNullOrWhiteSpace(Description)) feature.Description = Description; 73 | 74 | if (!string.IsNullOrWhiteSpace(contact?.Name)) feature.ContactName = contact.Name; 75 | 76 | if (!string.IsNullOrWhiteSpace(contact?.Email)) feature.ContactEmail = contact.Email; 77 | 78 | if (contact?.Url != null) feature.ContactUrl = new Uri(contact.Url); 79 | 80 | if (LicenseUrl != null) feature.LicenseUrl = LicenseUrl; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Settings/DocumenterSettingsScope.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Settings 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Reflection; 10 | using Models; 11 | 12 | // Based on JsConfig and JsConfigScope from ServiceStack 13 | public class DocumenterSettingsScope : IDisposable 14 | { 15 | /// 16 | /// The verbs used in the event of 'Any' being found for DTO. Default: GET, POST 17 | /// 18 | private IEnumerable standardVerbs; 19 | public IEnumerable ReplacementVerbs 20 | { 21 | get { return standardVerbs ?? new[] { "GET", "POST" }; } 22 | internal set { standardVerbs = value; } 23 | } 24 | 25 | /// 26 | /// The assemblies to containing implementations of RequestSpec and AbstractTypeSpec. Default: EntryAssembly 27 | /// 28 | private IEnumerable assemblies; 29 | public IEnumerable Assemblies 30 | { 31 | get { return assemblies ?? new[] { Assembly.GetEntryAssembly() }; } 32 | internal set { assemblies = value; } 33 | } 34 | 35 | /// 36 | /// The EnrichmentStrategy to use when populating collections. Default: Union 37 | /// 38 | private EnrichmentStrategy? collectionStrategy; 39 | public EnrichmentStrategy CollectionStrategy 40 | { 41 | get { return collectionStrategy ?? EnrichmentStrategy.Union; } 42 | internal set { collectionStrategy = value; } 43 | } 44 | 45 | /// 46 | /// The default notes to be set for a request/response object. 47 | /// 48 | public string FallbackNotes { get; internal set; } 49 | 50 | /// 51 | /// The default status codes to be set for a request/response object. 52 | /// 53 | public IEnumerable DefaultStatusCodes { get; internal set; } 54 | 55 | /// 56 | /// The default category to be set for a request/response object. 57 | /// 58 | public string FallbackCategory { get; internal set; } 59 | 60 | /// 61 | /// The default tags to be set for a request/response object. 62 | /// 63 | public IEnumerable DefaultTags { get; internal set; } 64 | 65 | /// 66 | /// The default contentTypes to be set for a request/response object. 67 | /// 68 | public IEnumerable DefaultContentTypes { get; internal set; } 69 | 70 | /// 71 | /// The default notes to be set for a route 72 | /// 73 | public string FallbackRouteNotes { get; internal set; } 74 | 75 | private bool disposed; 76 | private readonly DocumenterSettingsScope parent; 77 | 78 | [ThreadStatic] private static DocumenterSettingsScope head; 79 | 80 | internal static DocumenterSettingsScope Current => head ?? new DocumenterSettingsScope(); 81 | 82 | internal DocumenterSettingsScope() 83 | { 84 | parent = head; 85 | head = this; 86 | } 87 | 88 | public static void DisposeCurrent() => head?.Dispose(); 89 | 90 | public void Dispose() 91 | { 92 | if (!disposed) 93 | { 94 | disposed = true; 95 | head = parent; 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Settings/EnrichmentStrategy.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Settings 6 | { 7 | public enum EnrichmentStrategy 8 | { 9 | /// 10 | /// Each enricher will be called and result will be union of all 11 | /// 12 | Union = 0, 13 | 14 | /// 15 | /// Lower priority enrichers will only be called if property null/empty 16 | /// 17 | SetIfEmpty = 1 18 | } 19 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/AbstractRequestSpec.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using Extensions; 10 | using Html; 11 | using Models; 12 | 13 | /// 14 | /// Documentation class for a request DTO, including status codes etc 15 | /// 16 | /// DTO Type that is being decorated 17 | public abstract class AbstractRequestSpec : AbstractTypeSpec, IApiRequestSpec 18 | where T : class, new() 19 | { 20 | public Dictionary> StatusCodes { get; } 21 | public List Tags { get; } 22 | public Dictionary RouteNotes { get; } 23 | public Dictionary> ContentTypes { get; } 24 | 25 | public string Category { get; protected set; } 26 | 27 | /// 28 | /// Set tags for this DTO 29 | /// 30 | /// List of tags to set for this DTO 31 | protected void AddTags(params string[] tags) => Tags.AddRange(tags); 32 | 33 | /// 34 | /// Set status codes for this DTO that are available across all verbs 35 | /// 36 | /// List of status codes to set for this DTO 37 | protected void AddStatusCodes(params StatusCode[] statusCodes) 38 | => StatusCodes.UpdateList(Constants.GlobalSettingsKey, statusCodes); 39 | 40 | /// 41 | /// Set content-types for this DTO that are available across all verbs 42 | /// 43 | /// List of content types to set for this DTO 44 | protected void AddContentTypes(params string[] contentTypes) 45 | => ContentTypes.UpdateList(Constants.GlobalSettingsKey, contentTypes); 46 | 47 | /// 48 | /// Set route notes for this DTO for all verbs 49 | /// 50 | /// Notes to set for this DTO 51 | protected void AddRouteNotes(string notes) 52 | => RouteNotes[Constants.GlobalSettingsKey] = notes; 53 | 54 | /// 55 | /// Set status codes for this DTO that may be returned for specified verb 56 | /// 57 | /// The verb to set status codes for 58 | /// List of status codes to set for this DTO 59 | protected void AddStatusCodes(HttpVerbs verb, params StatusCode[] statusCodes) 60 | => StatusCodes.UpdateList(verb.ToString(), statusCodes); 61 | 62 | /// 63 | /// Set content-types for this DTO that are available for specified verb 64 | /// 65 | /// The verb to set content types for 66 | /// List of content types to set for this DTO 67 | protected void AddContentTypes(HttpVerbs verb, params string[] contentTypes) 68 | => ContentTypes.UpdateList(verb.ToString(), contentTypes); 69 | 70 | /// 71 | /// Set route notes for this DTO for specified verb 72 | /// 73 | /// The verb to set notes for 74 | /// Notes to set for this DTO 75 | protected void AddRouteNotes(HttpVerbs verb, string notes) 76 | => RouteNotes[verb.ToString()] = notes; 77 | 78 | protected AbstractRequestSpec() 79 | { 80 | StatusCodes = new Dictionary>(StringComparer.OrdinalIgnoreCase); 81 | RouteNotes = new Dictionary(StringComparer.OrdinalIgnoreCase); 82 | Tags = new List(); 83 | ContentTypes = new Dictionary>(StringComparer.OrdinalIgnoreCase); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/AbstractTypeSpec.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq.Expressions; 10 | using System.Reflection; 11 | using Extensions; 12 | 13 | /// 14 | /// Documentation class for default object exposed in API 15 | /// 16 | /// Object type that is being decorated 17 | public abstract class AbstractTypeSpec : IApiResource, IApiPropertyResource 18 | where T : class, new() 19 | { 20 | public string Title { get; protected set; } 21 | public string Description { get; protected set; } 22 | public string Notes { get; protected set; } 23 | 24 | private readonly Dictionary parameterLookup; 25 | 26 | protected AbstractTypeSpec() 27 | { 28 | parameterLookup = new Dictionary(); 29 | } 30 | 31 | public IProperty GetPropertySpec(MemberInfo pi) => parameterLookup.SafeGet(pi, (IProperty)null); 32 | 33 | protected IPropertyMetadata For(Expression> expression) 34 | { 35 | var parameter = PropertyMetadata.Create(expression); 36 | var memberInfo = parameter.MemberInfo; 37 | 38 | if (!(memberInfo is PropertyInfo) && !(memberInfo is FieldInfo)) 39 | throw new ArgumentException("For() only supports PropertyInfo or FieldInfo", nameof(expression)); 40 | 41 | parameterLookup.Add(memberInfo, parameter); 42 | return parameter; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/DocumentationClassLocator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using Extensions; 11 | using Logging; 12 | using Settings; 13 | 14 | public static class DocumentationClassLocator 15 | { 16 | private static readonly ILog log = LogManager.GetLogger(typeof(DocumentationClassLocator)); 17 | 18 | public static Dictionary GetLookup() 19 | { 20 | try 21 | { 22 | return GetLookup(DocumenterSettings.Assemblies.SelectMany(a => a.GetTypes())); 23 | } 24 | catch (Exception ex) 25 | { 26 | log.Error("Error getting documentation classes", ex); 27 | return new Dictionary(); 28 | } 29 | } 30 | 31 | public static Dictionary GetLookup(IEnumerable typesToScan) 32 | { 33 | try 34 | { 35 | var lookup = FindAllTypeSpecs(typesToScan) 36 | .ToDictionary(k => k.BaseType.GenericTypeArguments[0], 37 | v => (IApiResource)v.CreateInstance()); 38 | 39 | return lookup; 40 | } 41 | catch (Exception ex) 42 | { 43 | log.Error("Error getting documentation classes", ex); 44 | return new Dictionary(); 45 | } 46 | } 47 | 48 | private static IEnumerable FindAllTypeSpecs(IEnumerable types) 49 | { 50 | var target = typeof (AbstractTypeSpec<>); 51 | 52 | var foundTypes = from t in types 53 | let h = t.GetInheritanceHierarchy().Where(b => b.IsGenericType) 54 | where 55 | t.IsClass && !t.IsAbstract && t.IsVisible && 56 | h.Any(b => target.IsAssignableFrom(b.GetGenericTypeDefinition())) 57 | select t; 58 | 59 | return foundTypes; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/IApiResource.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System.Collections.Generic; 8 | using System.Reflection; 9 | using Models; 10 | 11 | public interface IApiResource 12 | { 13 | string Title { get; } 14 | string Description { get; } 15 | string Notes { get; } 16 | } 17 | 18 | public interface IApiRequestSpec : IApiResource 19 | { 20 | Dictionary> ContentTypes { get; } 21 | Dictionary> StatusCodes { get; } 22 | Dictionary RouteNotes { get; } 23 | List Tags { get; } 24 | string Category { get; } 25 | } 26 | 27 | public interface IApiPropertyResource 28 | { 29 | IProperty GetPropertySpec(MemberInfo pi); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/IFluentInterface.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.ComponentModel; 9 | 10 | // http://bit.ly/ifluentinterface 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public interface IFluentInterface 13 | { 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | Type GetType(); 16 | 17 | [EditorBrowsable(EditorBrowsableState.Never)] 18 | int GetHashCode(); 19 | 20 | [EditorBrowsable(EditorBrowsableState.Never)] 21 | string ToString(); 22 | 23 | [EditorBrowsable(EditorBrowsableState.Never)] 24 | bool Equals(object obj); 25 | } 26 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/IProperty.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using Models; 8 | 9 | public interface IProperty 10 | { 11 | string Title { get; set; } 12 | string Description { get; set; } 13 | bool? IsRequired { get; set; } 14 | PropertyConstraint Constraint { get; set; } 15 | bool? AllowMultiple { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/IPropertyMetadata.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.Linq.Expressions; 9 | 10 | public interface IPropertyMetadata : IProperty, IFluentInterface 11 | { 12 | IPropertyMetadata With(Expression> prop, TValue value); 13 | } 14 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/TypeSpec/PropertyMetadata.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.TypeSpec 6 | { 7 | using System; 8 | using System.Linq.Expressions; 9 | using System.Reflection; 10 | using FluentValidation.Internal; 11 | using Models; 12 | 13 | /// 14 | /// Represents data about a property of a type exposed via API 15 | /// 16 | public class PropertyMetadata : IPropertyMetadata 17 | { 18 | public string Title { get; set; } 19 | public string Description { get; set; } 20 | public bool? IsRequired { get; set; } 21 | public PropertyConstraint Constraint { get; set; } 22 | public bool? AllowMultiple { get; set; } 23 | 24 | internal MemberInfo MemberInfo { get; private set; } 25 | 26 | public IPropertyMetadata With(Expression> prop, TValue value) 27 | { 28 | (prop.GetMember() as PropertyInfo)?.SetValue(this, value); 29 | return this; 30 | } 31 | 32 | public PropertyMetadata(MemberInfo memberInfo) 33 | { 34 | MemberInfo = memberInfo; 35 | } 36 | 37 | public static PropertyMetadata Create(Expression> expression) 38 | => new PropertyMetadata(expression.GetMember()); 39 | } 40 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Utilities/ApiSpecMetadataUtilities.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Utilities 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using DTO; 10 | using Extensions; 11 | using Models; 12 | 13 | public static class ApiSpecMetadataUtilities 14 | { 15 | public static SpecMetadataResponse GenerateResponse(ApiDocumentation doc) 16 | { 17 | if (doc == null || doc.Resources.IsNullOrEmpty()) 18 | return null; 19 | 20 | var categories = GetCategoryLookup(doc); 21 | var tags = GetTagsLookup(doc); 22 | 23 | var response = new SpecMetadataResponse 24 | { 25 | Categories = 26 | categories.Select( 27 | grouping => new DtoGrouping { Key = grouping.Key, DtoNames = grouping.Select(v => v) }), 28 | Tags = 29 | tags.Select( 30 | grouping => new DtoGrouping { Key = grouping.Key, DtoNames = grouping.SelectMany(v => v) }), 31 | DtoNames = doc.Resources.Select(d => d.TypeName) 32 | }; 33 | 34 | return response; 35 | } 36 | 37 | private static ILookup> GetTagsLookup(ApiDocumentation doc) 38 | { 39 | var withTags = doc.Resources.Where(r => r.Tags != null).ToList(); 40 | var tags = withTags.SelectMany(r => r.Tags) 41 | .Distinct() 42 | .ToLookup(k => k, v => withTags.Where(r => r.Tags.Contains(v)) 43 | .Select(r => r.TypeName)); 44 | return tags; 45 | } 46 | 47 | private static ILookup GetCategoryLookup(ApiDocumentation doc) 48 | { 49 | var cats = doc.Resources.Where(r => !string.IsNullOrWhiteSpace(r.Category)) 50 | .ToLookup(k => k.Category, v => v.TypeName); 51 | return cats; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Utilities/EnumUtilities.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Utilities 6 | { 7 | using System; 8 | using Infrastructure; 9 | using Logging; 10 | 11 | public class EnumUtilities 12 | { 13 | private static readonly ILog log = LogManager.GetLogger(typeof(EnumUtilities)); 14 | 15 | /// 16 | /// Attempt to parse value to provided enum type. This is case insensitive. 17 | /// 18 | /// Enum type 19 | /// String value to parse 20 | /// Result containing parsed value if successful 21 | /// There is no generic constraint hence where T : struct, IConvertible constraint 22 | /// http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum 23 | public static Result SafeParse(string toParse) 24 | where T : struct, IConvertible 25 | { 26 | try 27 | { 28 | var result = (T)Enum.Parse(typeof(T), toParse, true); 29 | return Result.Success(result); 30 | } 31 | catch (ArgumentException e) 32 | { 33 | log.Warn($"Error parsing value {toParse} to enum {typeof(T).Name}", e); 34 | } 35 | 36 | return Result.Fail(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Utilities/MimeTypeUtilities.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Utilities 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using Extensions; 12 | 13 | public static class MimeTypeUtilities 14 | { 15 | private static readonly Dictionary mimeDictionary; 16 | static MimeTypeUtilities() 17 | { 18 | // There's no nice way to get mime type ("application/json") from type ("json"). 19 | // ServiceStack.MimeTypes class has all known types as constants so build dictionary of these 20 | 21 | mimeDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 22 | var fieldInfos = typeof(MimeTypes).GetFields(BindingFlags.Public | BindingFlags.Static); 23 | 24 | var strType = typeof(string); 25 | foreach (var fi in fieldInfos.Where(f => f.IsLiteral && !f.IsInitOnly && f.FieldType == strType)) 26 | mimeDictionary.Add(fi.Name, fi.GetValue(null).ToString()); 27 | } 28 | 29 | public static string GetMimeType(string type) => mimeDictionary.SafeGet(type, () => MimeTypes.GetMimeType(type)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Validators/ApiContactValidator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Validators 6 | { 7 | using FluentValidation; 8 | using Models; 9 | 10 | public class ApiContactValidator : AbstractValidator 11 | { 12 | public ApiContactValidator() 13 | { 14 | RuleFor(c => c.Name).NotEmpty(); 15 | RuleFor(c => c.Email).NotEmpty().EmailAddress(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Validators/ApiSpecConfigValidator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Validators 6 | { 7 | using FluentValidation; 8 | using Settings; 9 | 10 | public class ApiSpecConfigValidator : AbstractValidator 11 | { 12 | public ApiSpecConfigValidator() 13 | { 14 | RuleFor(c => c.Contact).NotNull().SetValidator(new ApiContactValidator()); 15 | RuleFor(c => c.Description).NotEmpty(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/Validators/ApiSpecSettingsValidator.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.Validators 6 | { 7 | using FluentValidation; 8 | 9 | public class ApiSpecSettingsValidator : AbstractValidator 10 | { 11 | public ApiSpecSettingsValidator() 12 | { 13 | RuleFor(c => c.ContactName).NotEmpty(); 14 | RuleFor(c => c.ContactEmail).NotEmpty().EmailAddress(); 15 | RuleFor(c => c.Description).NotEmpty(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/XmlDocumentation/IXmlDocumentationLookup.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.XmlDocumentation 6 | { 7 | using System.Reflection; 8 | 9 | public interface IXmlDocumentationLookup 10 | { 11 | XmlMember GetXmlMember(MemberInfo member); 12 | } 13 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/XmlDocumentation/IXmlDocumentationReader.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.XmlDocumentation 6 | { 7 | public interface IXmlDocumentationReader 8 | { 9 | XmlDocumentation GetXmlDocumentation(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/XmlDocumentation/XmlDocumentationLookup.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.XmlDocumentation 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Reflection; 10 | using Logging; 11 | 12 | public class XmlDocumentationLookup : IXmlDocumentationLookup 13 | { 14 | private readonly ILog log = LogManager.GetLogger(typeof(XmlDocumentationLookup)); 15 | private readonly Dictionary lookup; 16 | 17 | public XmlDocumentationLookup(IXmlDocumentationReader documentationReader) 18 | { 19 | documentationReader.ThrowIfNull(); 20 | 21 | lookup = PopulateLookup(documentationReader.GetXmlDocumentation()); 22 | } 23 | 24 | public XmlMember GetXmlMember(MemberInfo member) 25 | { 26 | if (lookup == null) 27 | return XmlMember.Default; 28 | 29 | // Get the specific lookup name 30 | var name = member.GetMemberElementName(); 31 | 32 | // Lookup the value 33 | XmlMember xmlMember; 34 | return lookup.TryGetValue(name, out xmlMember) ? xmlMember : XmlMember.Default; 35 | } 36 | 37 | private Dictionary PopulateLookup(XmlDocumentation xmlDocumentation) 38 | { 39 | if (xmlDocumentation?.Members == null) 40 | { 41 | log.Info("No xml documentation available."); 42 | return null; 43 | } 44 | 45 | return xmlDocumentation.Members.ToDictionary(k => k.Name, v => v); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/ServiceStack.IntroSpec/ServiceStack.IntroSpec/XmlDocumentation/XmlDocumentationReader.cs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | namespace ServiceStack.IntroSpec.XmlDocumentation 6 | { 7 | using System; 8 | using System.IO; 9 | using System.Reflection; 10 | using System.Xml.Serialization; 11 | using Logging; 12 | 13 | public class XmlDocumentationReader : IXmlDocumentationReader 14 | { 15 | private readonly ILog log = LogManager.GetLogger(typeof(XmlDocumentationReader)); 16 | 17 | public XmlDocumentation GetXmlDocumentation() 18 | { 19 | try 20 | { 21 | var assemblyName = Assembly.GetEntryAssembly().GetName().Name; 22 | log.Debug($"Attempting to read {assemblyName} XML file"); 23 | using (var contents = File.OpenText($"{assemblyName}.xml")) 24 | { 25 | var serializer = new XmlSerializer(typeof(XmlDocumentation)); 26 | return (XmlDocumentation)serializer.Deserialize(contents); 27 | } 28 | } 29 | catch (Exception e) 30 | { 31 | log.Error($"Error getting xml documentation.", e); 32 | return null; 33 | } 34 | } 35 | } 36 | } --------------------------------------------------------------------------------