Skip to content

Commit 3482609

Browse files
committed
Temp commit
1 parent c9597cc commit 3482609

File tree

5 files changed

+310
-10
lines changed

5 files changed

+310
-10
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System.Linq;
2+
3+
namespace Typewriter
4+
{
5+
using ApiDoctor.Validation;
6+
using ApiDoctor.Validation.Error;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using System.Text;
11+
12+
internal static class CsdlExtensionMethods
13+
{
14+
15+
public static string RequestUriPathOnly(this MethodDefinition method, string[] baseUrlsToRemove, IssueLogger issues)
16+
{
17+
var path = method.Request.FirstLineOnly().TextBetweenCharacters(' ', '?').TrimEnd('/');
18+
19+
if (baseUrlsToRemove != null)
20+
{
21+
foreach (var baseUrl in baseUrlsToRemove)
22+
{
23+
if (!string.IsNullOrEmpty(baseUrl) && path.StartsWith(baseUrl, StringComparison.OrdinalIgnoreCase))
24+
{
25+
path = path.Substring(baseUrl.Length);
26+
}
27+
}
28+
}
29+
30+
// just in case there's a stray example that doesn't match, at least chop the domain part off.
31+
foreach (var scheme in new[] { "https://", "http://" })
32+
{
33+
if (path.StartsWith(scheme, StringComparison.OrdinalIgnoreCase))
34+
{
35+
int pathStartIndex = path.IndexOf('/', scheme.Length);
36+
if (pathStartIndex != -1)
37+
{
38+
path = path.Substring(pathStartIndex);
39+
break;
40+
}
41+
}
42+
}
43+
44+
// Normalize variables in the request path
45+
path = path.ReplaceTextBetweenCharacters('{', '}', "var");
46+
47+
if (method.RequestMetadata.SampleKeys != null)
48+
{
49+
foreach (var key in method.RequestMetadata.SampleKeys)
50+
{
51+
path = path.Replace("/" + key, "/{var}");
52+
}
53+
}
54+
55+
// Normalize function params
56+
var substitutions = new Dictionary<string, string>();
57+
var parenIndex = path.IndexOf('(');
58+
for (int i = 0; i < path.Length; i++)
59+
{
60+
if (path[i] == '(')
61+
{
62+
// this is the start of a function. let's find the closing paren.
63+
var close = path.IndexOf(')', i);
64+
if (close > -1)
65+
{
66+
var inner = path.Substring(i + 1, close - i - 1);
67+
substitutions[inner] = NormalizeFunctionParameters(inner, issues);
68+
i = close;
69+
}
70+
}
71+
}
72+
73+
foreach (var sub in substitutions)
74+
{
75+
path = path.Replace(sub.Key, sub.Value);
76+
}
77+
78+
// Rewrite path syntax into what it logically means
79+
path = path.ReplaceTextBetweenCharacters(':', ':', "/children/{var}", requireSecondChar: false, removeTargetChars: true);
80+
81+
return path;
82+
}
83+
84+
private static string NormalizeFunctionParameters(string funcParams, IssueLogger issues)
85+
{
86+
// foo=bar, baz ='qux',x= 9
87+
var normalized = new StringBuilder();
88+
var allParams = funcParams.Split(',');
89+
for (int i = 0; i < allParams.Length; i++)
90+
{
91+
var param = allParams[i].Trim();
92+
var kvp = param.Split('=');
93+
if (kvp.Length != 2)
94+
{
95+
issues.Error(ValidationErrorCode.ParameterParserError, $"Malformed function params {funcParams}");
96+
return funcParams;
97+
}
98+
99+
allParams[i] = kvp[0].Trim() + "={var}";
100+
}
101+
102+
return string.Join(",", allParams.OrderBy(p => p));
103+
}
104+
105+
public static string HttpMethodVerb(this MethodDefinition method)
106+
{
107+
HttpParser parser = new HttpParser();
108+
var request = parser.ParseHttpRequest(method.Request);
109+
return request.Method;
110+
111+
}
112+
113+
internal static void AppendWithCondition(this System.Text.StringBuilder sb, bool condition, string text, string prefixIfExistingContent = null)
114+
{
115+
if (condition)
116+
{
117+
if (sb.Length > 0 && prefixIfExistingContent != null)
118+
sb.Append(prefixIfExistingContent);
119+
sb.Append(text);
120+
}
121+
}
122+
123+
/// <summary>
124+
/// Merge two EntityFramework instances together into the first framework
125+
/// </summary>
126+
/// <param name="framework1"></param>
127+
/// <param name="framework2"></param>
128+
internal static EntityFramework MergeWith(this EntityFramework framework1, EntityFramework framework2)
129+
{
130+
ObjectGraphMerger<EntityFramework> merger = new ObjectGraphMerger<EntityFramework>(framework1, framework2);
131+
var edmx = merger.Merge();
132+
133+
// Clean up bindingParameters on actions and methods to be consistently the same
134+
foreach (var schema in edmx.DataServices.Schemas)
135+
{
136+
foreach (var action in schema.Actions)
137+
{
138+
foreach (var param in action.Parameters.Where(x => x.Name == "bindingParameter" || x.Name == "this"))
139+
{
140+
param.Name = "bindingParameter";
141+
param.IsNullable = null;
142+
}
143+
}
144+
foreach (var func in schema.Functions)
145+
{
146+
foreach (var param in func.Parameters.Where(x => x.Name == "bindingParameter" || x.Name == "this"))
147+
{
148+
param.Name = "bindingParameter";
149+
param.IsNullable = null;
150+
}
151+
}
152+
}
153+
154+
return edmx;
155+
156+
157+
}
158+
159+
160+
161+
162+
}
163+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using ApiDoctor.Publishing.CSDL;
2+
using ApiDoctor.Validation;
3+
using ApiDoctor.Validation.Error;
4+
using ApiDoctor.Validation.OData;
5+
using NLog;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
13+
namespace Typewriter
14+
{
15+
internal class DocAnnotationWriter : ApiDoctor.Publishing.CSDL.CsdlWriter// or this a helper, will name this later
16+
{
17+
private static Logger Logger => LogManager.GetLogger("DocAnnotationWriter");
18+
private string csdl;
19+
20+
internal IssueLogger IssueLogger { get; set; }
21+
internal DocSet DocSet { get; set; }
22+
23+
private readonly CsdlWriterOptions options;
24+
25+
internal DocAnnotationWriter(DocSet docSet, CsdlWriterOptions options, string csdl) : base(docSet, options)
26+
{
27+
this.csdl = csdl;
28+
this.DocSet = docSet;
29+
this.options = options; // Can change the base access modifier so we could use it.
30+
}
31+
32+
private async Task<string> PublishToStringAsync(IssueLogger issues)
33+
{
34+
// Step 1: Generate an EntityFramework OM from the documentation and/or template file
35+
EntityFramework framework = CreateEntityFrameworkFromDocs(issues);
36+
if (null == framework)
37+
return string.Empty;
38+
39+
if (!string.IsNullOrEmpty(options.MergeWithMetadataPath))
40+
{
41+
EntityFramework secondFramework = CreateEntityFrameworkFromDocs(issues, options.MergeWithMetadataPath, generateFromDocs: false);
42+
framework = framework.MergeWith(secondFramework);
43+
outputFilenameSuffix += "-merged";
44+
}
45+
46+
// Step 1a: Apply an transformations that may be defined in the documentation
47+
if (!string.IsNullOrEmpty(options.TransformOutput))
48+
{
49+
PublishSchemaChangesConfigFile transformations = DocSet.TryLoadConfigurationFiles<PublishSchemaChangesConfigFile>(options.DocumentationSetPath).Where(x => x.SchemaChanges.TransformationName == options.TransformOutput).FirstOrDefault();
50+
if (null == transformations)
51+
{
52+
throw new KeyNotFoundException($"Unable to locate a transformation set named {options.TransformOutput}. Aborting.");
53+
}
54+
55+
string[] versionsToPublish = options.Version?.Split(new char[] { ',', ' ' });
56+
framework.ApplyTransformation(transformations.SchemaChanges, versionsToPublish);
57+
if (!string.IsNullOrEmpty(options.Version))
58+
{
59+
outputFilenameSuffix += $"-{options.Version}";
60+
}
61+
}
62+
63+
if (options.Sort)
64+
{
65+
// Sorts the objects in collections, so that we have consistent output regardless of input
66+
framework.SortObjectGraph();
67+
}
68+
69+
if (options.ValidateSchema)
70+
{
71+
framework.ValidateSchemaTypes();
72+
}
73+
74+
// Step 2: Generate XML representation of EDMX
75+
string xmlData = null;
76+
if (options.Formats.HasFlag(MetadataFormat.EdmxOutput))
77+
{
78+
xmlData = ODataParser.Serialize<EntityFramework>(framework, options.AttributesOnNewLines);
79+
}
80+
else if (options.Formats.HasFlag(MetadataFormat.SchemaOutput))
81+
{
82+
xmlData = ODataParser.Serialize<Schema>(framework.DataServices.Schemas.First(), options.AttributesOnNewLines);
83+
}
84+
85+
return xmlData;
86+
}
87+
}
88+
89+
internal static class AnnotationHelper
90+
{
91+
private static Logger Logger => LogManager.GetLogger("AnnotationHelper");
92+
93+
internal static string ApplyAnnotationsToCsdl(string csdl, Options options)
94+
{
95+
// Get DocSet
96+
DocSet docs = GetDocSet(options, new IssueLogger());
97+
// Create CsdlWriterOptions
98+
var csdlWriterOptions = new CsdlWriterOptions() { DocumentationSetPath = options.DocsRoot + "\\api-reference\\v1.0\\" };
99+
100+
// Create DocAnnotationWriter
101+
DocAnnotationWriter docWriter = new DocAnnotationWriter(docs, csdlWriterOptions, csdl);
102+
103+
throw new NotImplementedException();
104+
}
105+
106+
private static DocSet GetDocSet(Options options, IssueLogger issues)
107+
{
108+
Logger.Info("Opening documentation from {0}", options.DocsRoot);
109+
DocSet docSet = null;
110+
111+
try
112+
{
113+
docSet = new DocSet(options.DocsRoot + "\\api-reference\\v1.0\\");
114+
}
115+
catch (System.IO.FileNotFoundException ex)
116+
{
117+
Logger.Error(ex.Message);
118+
return null; // Hmmmmm. TODO
119+
}
120+
121+
Logger.Info("Scanning documentation files");
122+
var stopwatch = new Stopwatch();
123+
docSet.ScanDocumentation(string.Empty, issues);
124+
stopwatch.Stop();
125+
Logger.Info($"Took {stopwatch.Elapsed} to parse {docSet.Files.Length} source files.");
126+
127+
return docSet;
128+
}
129+
}
130+
}

src/Typewriter/Options.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
6-
using CommandLine;
1+
using CommandLine;
72

83
namespace Typewriter
94
{
@@ -28,5 +23,8 @@ class Options
2823

2924
[Option('o', "output", Default= ".", HelpText = "Path to output folder")]
3025
public string Output { get; set; }
26+
27+
[Option('d', "docs", Default = ".", HelpText = "Path to the root of the documentation repo folder")]
28+
public string DocsRoot { get; set; }
3129
}
32-
}
30+
}

src/Typewriter/Program.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using NLog.Targets;
1212
using Vipr.Core;
1313
using Vipr.Reader.OData.v4;
14+
using ApiDoctor.Publishing;
1415

1516
namespace Typewriter
1617
{
@@ -32,12 +33,15 @@ private static void GenerateSDK(Options options)
3233

3334
SetupLogging(options.Verbosity);
3435

35-
var csdlContents = MetadataResolver.GetMetadata(options.Metadata);
36+
string csdlContents = MetadataResolver.GetMetadata(options.Metadata);
3637

3738
// Clean up EDMX to work with the generators assumptions.
38-
var processCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
39+
string processedCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
3940

40-
var files = MetadataToClientSource(processCsdlContents, options.Language);
41+
// Inject documentation annotations into the CSDL using ApiDoctor.
42+
string csdlWithDocAnnotations = AnnotationHelper.ApplyAnnotationsToCsdl(processedCsdlContents, options);
43+
44+
var files = MetadataToClientSource(csdlWithDocAnnotations, options.Language);
4145
FileWriter.WriteAsync(files, options.Output);
4246

4347
stopwatch.Stop();

src/Typewriter/Typewriter.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
</ItemGroup>
4444
<ItemGroup>
4545
<Compile Include="ConfigurationProvider.cs" />
46+
<Compile Include="CsdlExtensionMethods.cs" />
47+
<Compile Include="DocAnnotationWriter.cs" />
4648
<Compile Include="FileWriter.cs" />
4749
<Compile Include="MetadataPreprocessor.cs" />
4850
<Compile Include="MetadataResolver.cs" />
@@ -68,6 +70,9 @@
6870
</ProjectReference>
6971
</ItemGroup>
7072
<ItemGroup>
73+
<PackageReference Include="ApiDoctor.Publishing">
74+
<Version>1.0.0-CI-20181018-040801</Version>
75+
</PackageReference>
7176
<PackageReference Include="CommandLineParser">
7277
<Version>2.2.1</Version>
7378
</PackageReference>

0 commit comments

Comments
 (0)