PageRenderTime 71ms CodeModel.GetById 15ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 1ms

/specificator/performer.cs

https://bitbucket.org/jeyjen/specificator
C# | 472 lines | 412 code | 49 blank | 11 comment | 74 complexity | d7ac6363ba526f2140fa7f74fb6b5f98 MD5 | raw file
  1using jeyjen.extension;
  2using Mono.Cecil;
  3using Newtonsoft.Json.Linq;
  4using System;
  5using System.Collections;
  6using System.Collections.Generic;
  7using System.IO;
  8using System.Linq;
  9using System.Text;
 10using System.Xml.Linq;
 11
 12namespace specificator
 13{
 14    public class performer
 15    {
 16        Dictionary<string, details> _methods;
 17        Dictionary<string, details> _classes;
 18        Dictionary<string, details> _properties;
 19        Dictionary<string, JObject> _definitions;
 20        private string _name;
 21
 22        public performer()
 23        {
 24            _methods = new Dictionary<string, details>();
 25            _classes = new Dictionary<string, details>();
 26            _properties = new Dictionary<string, details>();
 27            _definitions = new Dictionary<string, JObject>();
 28        }
 29        public string name
 30        {
 31            get
 32            {
 33                return _name;
 34            }
 35        }
 36
 37        public string swagger(string path, string version)
 38        {
 39            if (!File.Exists(path))
 40                throw new Exception("assembly does not found");
 41
 42            var file = Path.GetFileNameWithoutExtension(path);
 43            var xml = Path.Combine(Path.GetDirectoryName(path), "{0}.xml".format(file));
 44            if (File.Exists(xml))
 45                parse_xml_doc(xml);
 46
 47            return prepare_specification(path, version);
 48        }
 49        
 50        private void parse_xml_doc(string path)
 51        {
 52            var doc = XDocument.Parse(File.ReadAllText(path));
 53            var ms = doc.Element("doc")
 54                .Element("members")
 55                .Elements("member");
 56            foreach (var m in ms)
 57            {
 58                var d = new details();
 59                var summary = m.Element("summary");
 60                if (!summary.is_null())
 61                    d.summary = summary.Value.Trim();
 62                var see = m.Element("see");
 63                if (!see.is_null())
 64                {
 65                    var href = see.Attribute("href");
 66                    if (!href.is_null())
 67                    {
 68                        d.href = href.Value.Trim();
 69                    }
 70                }
 71                var pars = m.Elements("param");
 72                foreach (var p in pars)
 73                {
 74                    var n = p.Attribute("name");
 75                    if (!n.is_null())
 76                    {
 77                        d.members.Add(n.Value.Trim(), p.Value.Trim());
 78                    }
 79                }
 80                var returns = m.Element("returns");
 81                if (!returns.is_null())
 82                {
 83                    d.returns = returns.Value.Trim();
 84                }
 85
 86
 87                var name = m.Attribute("name").Value.Trim();
 88                if (name.StartsWith("M:"))
 89                {
 90                    var idx = name.IndexOf('(');
 91                    if (idx >= 0)
 92                    {
 93                        name = name.Substring(2, idx - 2);
 94                    }
 95                    
 96                    var ps = name.Split('.');
 97                    _methods.Add("{0}.{1}".format(ps[ps.Length - 2], ps[ps.Length - 1]), d);
 98                }
 99                else if (name.StartsWith("T:"))
100                {
101                    name = name.Substring(2);
102                    
103                    _classes.Add(name, d);
104                }
105                else if (name.StartsWith("P:"))
106                {
107                    name = name.Substring(2);
108                    var ps = name.Split('.');
109                    _properties.Add(name, d);
110                }
111            }
112        }
113        private string prepare_specification(string assembly, string version)
114        {
115            var name = "";
116            var spec = new JObject();
117            spec.Add("swagger", "2.0");
118            spec.Add("host", "host.com");
119            spec.Add("schemes", new JArray("http", "https"));
120
121            var types = AssemblyDefinition
122                .ReadAssembly(assembly)
123                .MainModule
124                .Types;
125
126            var info = new JObject();
127            spec.Add("info", info);
128            info.Add("version", version);
129            foreach (var t in types)
130            {
131                if (!t.BaseType.is_null())
132                {
133                    if (t.BaseType.Name.is_equals("mq_node"))
134                    {
135                        name = t.Name;
136                        _name = name;
137                        var desc = "";
138                        if (_classes.ContainsKey(t.FullName))
139                        {
140                            desc = _classes[t.FullName].summary;
141                        }
142                        info.Add("description", desc);
143                        info.Add("title", name);
144                        break;
145                    }
146                }
147            }
148            spec.Add("basePath", "/{0}".format(name));
149
150            var tags = new List<JObject>();
151            var paths = new Dictionary<string, JObject>();
152            foreach (var t in types)
153            {
154                // определение контейнера
155                if (t.BaseType.is_null())
156                    continue;
157                if (!t.BaseType.Name.Contains("container`"))
158                    continue;
159                var container_name = t.Name;
160                if (container_name.is_equals("index"))
161                    container_name = "";
162
163                var tag = new JObject();
164                tag.Add("name", container_name);
165                if (_classes.ContainsKey(t.FullName))
166                {
167                    var d = _classes[t.FullName];
168                    tag.Add("description", d.summary);
169                    if (!d.href.is_null_or_empty())
170                    {
171                        var ext_doc = new JObject();
172                        ext_doc.Add("description", "детальнее");
173                        ext_doc.Add("url", d.href);
174                        tag.Add("externalDocs", ext_doc);
175                    }
176                }
177                tags.Add(tag);
178
179                // опрделение операций
180                foreach (var method in t.Methods.Where((_) => { return _.IsPublic && !_.IsStatic && !_.IsConstructor; }))
181                {
182                    if (method.Name.is_equals("ToString")
183                        || method.Name.is_equals("Equals")
184                        || method.Name.is_equals("GetHashCode")
185                        || method.Name.is_equals("GetType"))
186                        continue;
187                    var operation_name = method.Name.ToLower();
188
189                    var sb = new StringBuilder();
190                    if (!container_name.is_null_or_empty())
191                        sb.AppendFormat("/{0}", container_name);
192                    sb.AppendFormat("/{0}", operation_name);
193                    var path = sb.ToString();
194                    var operation_id = path.Replace('/', '_')
195                        .Substring(1);
196
197                    var summary = "";
198
199                    details md = new details();
200                    var key = "{0}.{1}".format(t.Name, method.Name);
201                    if (_methods.ContainsKey(key))
202                    {
203                        md = _methods[key];
204                        summary = md.summary;
205                    }
206                    var parameters = new JArray();
207                    //var produces = new JArray("multipart/form-data");
208                    //var consumes = new JArray("multipart/form-data");
209                    var parameter = new JObject();
210                    parameters.Add(parameter);
211                    parameter.Add("in", "body");
212                    parameter.Add("name", "body");
213                    var schema = new JObject();
214                    parameter.Add("schema", schema);
215                    
216                    var properties = new JObject();
217                    schema.Add("type", "object");
218                    schema.Add("properties", properties);
219                    var required = new JArray();
220                    
221
222                    // required arguments
223                    foreach (var p in method.Parameters.OrderBy(_ => { return _.Name; }) )
224                    {
225                        if (p.IsOptional)
226                            continue;
227                        required.Add(p.Name);
228
229                        var pd = define_equals_type(p.ParameterType);
230                        if (md.members.ContainsKey(p.Name))
231                            pd.Add("description", md.members[p.Name]);
232
233                        properties.Add(p.Name, pd);
234                    }
235
236                    if(required.Count > 0)
237                        schema.Add("required", required);
238
239                    // optional arguments
240                    foreach (var p in method.Parameters.OrderBy(_ => { return _.Name; }))
241                    {
242                        if (!p.IsOptional)
243                            continue;
244
245                        var pd = define_equals_type(p.ParameterType);
246                        if (md.members.ContainsKey(p.Name))
247                            pd.Add("description", md.members[p.Name]);
248
249                        properties.Add(p.Name, pd);
250                    }
251                    var prefix = new JObject();
252                    prefix.Add("name", "mq_prefix");
253                    prefix.Add("in", "header");
254                    prefix.Add("required", false);
255                    prefix.Add("type", "string");
256                    var v = new JArray() {"_dev", "common", "test.even", "test.odd", "preprod" };
257                    prefix.Add("enum", v);
258                    prefix.Add("default", "");
259                    prefix.Add("description", "префикс контура");
260                    parameters.Add(prefix);
261
262                    var mq_id = new JObject();
263                    mq_id.Add("name", "mq_id");
264                    mq_id.Add("in", "header");
265                    mq_id.Add("required", false);
266                    mq_id.Add("type", "string");
267                    parameters.Add(mq_id);
268
269                    var r200 = new JObject();
270                    var r500 = new JObject();
271                    var responses = new JObject();
272                    responses.Add("200", r200);
273                    responses.Add("500", r500);
274
275                    if (md.returns.is_null_or_empty())
276                        r200.Add("description", "success");
277                    else
278                        r200.Add("description", md.returns);
279
280                    var r_200_schema = new JObject();
281                    r200.Add("schema", r_200_schema);
282                    r_200_schema.Add("type", "object");
283                    var r_200_properties = new JObject();
284                    r_200_schema.Add("properties", r_200_properties);
285                    // добавить result
286                    var ttt = define_result_type(method.ReturnType);
287                    
288                    r_200_properties.Add("result", ttt);
289                    
290                    r500.Add("description", "internal error");
291                    var r_500_schema = new JObject();
292                    r500.Add("schema", r_500_schema);
293                    r_500_schema.Add("type", "object");
294                    var r_500_properties = new JObject();
295                    r_500_schema.Add("properties", r_500_properties);
296                    var property500 = new JObject();
297                    r_500_properties.Add("message", property500);
298                    property500.Add("type", "string");
299                    property500.Add("description", "сообщение об ошибке");
300
301                    var post = new JObject();
302
303                    post.Add("tags", new JArray(container_name));
304                    post.Add("summary", summary);
305                    //post.Add("description", summary);
306                    post.Add("operationId", operation_id);
307                    //post.Add("produces", produces);
308                    //post.Add("consumes", consumes);
309                    post.Add("parameters", parameters);
310                    post.Add("responses", responses);
311
312                    var path_content = new JObject();
313                    path_content.Add("post", post);
314                    paths.Add(path, path_content);
315                }
316            }
317
318            tags = tags
319                .OrderBy((t) => { return t.Value<string>("name"); })
320                .ToList();
321
322            spec.Add("tags", new JArray(tags));
323
324            
325            var ps = new JObject();
326
327            var pks = paths.Keys.OrderBy((k) => { return k; });
328
329            foreach (var p in pks)
330                ps.Add(p, paths[p]);
331            spec.Add("paths", ps);
332
333            var definitions = new JObject();
334            foreach (var i in _definitions)
335            {
336                definitions.Add(i.Key, i.Value);
337            }
338            spec.Add("definitions", definitions);
339
340            return spec.ToString();
341        }
342        private JObject define_result_type(TypeReference t)
343        {
344            if (t.FullName.Contains("System.Threading.Tasks.Task`"))
345            {
346                var prop = t.GetType().GetProperty("GenericArguments");
347                var d = (IList)prop.GetValue(t);
348                return define_equals_type((TypeReference)d[0]);
349            }
350            else if (t.FullName.is_equals("System.Void"))
351            {
352                return new JObject();
353            }
354            else if (t.FullName.is_equals("System.Threading.Tasks.Task"))
355            {
356                return new JObject();
357            }
358            return define_equals_type(t);
359        }
360        private JObject define_equals_type(TypeReference t)
361        {
362            var res = new JObject();
363            if (t.FullName.StartsWith("System.Collections.Generic.List`")
364                || t.FullName.StartsWith("System.Collections.Generic.HashSet`"))
365            {
366                var prop = t.GetType().GetProperty("GenericArguments");
367                var d = (IList)prop.GetValue(t);
368                var arg_type = (TypeReference)d[0];
369
370                res.Add("items", define_equals_type(arg_type));
371                res.Add("type", "array");
372            }
373            else if (t.FullName.StartsWith("System.Collections.Generic.Dictionary`"))
374            {
375                res.Add("type", "object");
376            }
377            else if (t.FullName.is_equals("Newtonsoft.Json.Linq.JObject"))
378            {
379                res.Add("type", "object");
380            }
381            else if (t.FullName.is_equals("Newtonsoft.Json.Linq.JArray"))
382            {
383                res.Add("type", "array");
384                var o = new JObject();
385                res.Add("items", o);
386                o.Add("type", "object");
387            }
388            else if (t.IsArray)
389            {
390                var arg_type = t.GetElementType();
391                res.Add("items", define_equals_type(arg_type));
392                res.Add("type", "array");
393            }
394            else if (t.FullName.is_equals("System.String"))
395            {
396                res.Add("type", "string");
397            }
398            else if (t.FullName.Contains("System.SByte")
399                || t.FullName.Contains("System.Byte")
400                || t.FullName.Contains("System.Char")
401                || t.FullName.Contains("System.Int16")
402                || t.FullName.Contains("System.UInt16")
403                || t.FullName.Contains("System.Int32")
404                || t.FullName.Contains("System.UInt32")
405                || t.FullName.Contains("System.Int64")
406                || t.FullName.Contains("System.UInt64")
407                )
408            {
409                res.Add("type", "integer");
410            }
411            else if (t.FullName.Contains("System.Boolean"))
412            {
413                res.Add("type", "boolean");
414            }
415            else if (t.FullName.Contains("System.Single")
416                || t.FullName.Contains("System.Double")
417                || t.FullName.Contains("System.Decimal"))
418            {
419                res.Add("type", "number");
420            }
421
422            else // object
423            {
424                if (!_definitions.ContainsKey(t.Name))
425                {
426                    var def = new JObject();
427                    def.Add("type", "object");
428                    if (_classes.ContainsKey(t.FullName))
429                    {
430                        def.Add("description", _classes[t.FullName].summary);
431                    }
432                    var props = new JObject();
433                    def.Add("properties", props);
434                    var tmp_obj = (TypeDefinition)t;
435
436                    var ts = new List<TypeDefinition>();
437                    ts.Add(tmp_obj);
438                    while (!tmp_obj.BaseType.FullName.is_equals("System.Object"))
439                    {
440                        tmp_obj = (TypeDefinition)tmp_obj.BaseType;
441                        ts.Add(tmp_obj);
442                    }
443                    foreach (var obj in ts)
444                    {
445                        foreach (var i in obj.Properties)
446                        {
447                            var idx = i.FullName.IndexOf(' ');
448                            var k = i.FullName
449                                .Substring(idx + 1)
450                                .Replace("::", ".")
451                                .Replace("()", "");
452
453                            var ed = define_equals_type(i.PropertyType);
454                            var ttt = ed.Value<string>("type");
455                            if (!ttt.is_null()
456                                && _properties.ContainsKey(k))
457                            {
458                                ed.Add("description", _properties[k].summary);
459                            }
460
461                            props.Add(i.Name, ed);
462                        }
463                    }
464                    // отсортировать свойства
465                    _definitions.Add(t.Name, def);
466                }
467                res.Add("$ref", "#/definitions/{0}".format(t.Name));
468            }
469            return res;
470        }
471    }
472}