Self-maintaining documentation for HTTP Rest APIs

So, I’ve got an API. I probably want to expose the basics on usage to the public, but I sure as hell don’t want to write documentation. My solution? Self-documenting code. ❤ reflection.

There are a few base classes referenced that I'm using to determine items which are allowed to be accessible publicly, and some that aren't. Maybe my code sucks, whatever, but it works. It certainly makes a few assumptions about what & how you're doing things (like Get/Post method names for the accepted http verbs).

Anyhow, it works for me and spits out a fancy JSON object that can be inspected via fiddler or chrome dev tools, which is enough to at least get started with consuming a new API. Sure beats randomly trying URLs and input/outputs.

 [Unauthenticated]
     public class ConfigController : SiteApiController
     {
          public ConfigData Get()
          {
               var types = Assembly.GetAssembly(typeof(ContractBase))
                    .GetTypes().Where(x => x.IsClass 
                         && typeof(PublicItem).IsAssignableFrom(x) 
                         && x != typeof(PublicItem));
               var parser = new ObjectParser();
               var d = new Dictionary();
               foreach (var type in types)
               {
                  d.Add(type.Name,parser.ParseObject(type));
               }
               var etypes = Assembly.GetAssembly(typeof(ContractBase)).GetTypes().Where(x => x.IsEnum);
               var e = new Dictionary();
               foreach (var etype in etypes)
               {
                    e.Add(etype.Name, parser.ParseEnum(etype));
               }
               var controllers = Assembly.GetAssembly(typeof(SiteApiController))
                    .GetTypes().Where(x => typeof(SiteApiController).IsAssignableFrom(x) && x != typeof(SiteApiController));
               var m = new Dictionary();
               foreach (var c in controllers)
               {
                    m.Add("/api/" + c.Name.Replace("Controller", ""), parser.ParseController(c));
               }

               return new ConfigData() {Classes = d, Enums = e, Methods = m};
          }
          public class ConfigData
          {
               public dynamic Classes { get; set; }
               public dynamic Enums { get; set; }
               public dynamic Methods { get; set; }
          }
     }
internal class ObjectParser
     {
          public dynamic ParseObject(Type type)
          {
               var props =
                    type
                       .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty |
                                          BindingFlags.GetProperty).Where(prop =>
                         !Attribute.IsDefined(prop, typeof(NotPublicAttribute)))
                         .ToArray(); ;

               var dict = new Dictionary();
               foreach (var prop in props)
               {
                    dict[prop.Name] = GetReadableType(prop.PropertyType);
               }
               return dict;
          }
          private string GetReadableType(Type type)
          {
               string ptype = type.Name;
               if (type.IsGenericType)
                    {
                         ptype = ptype.Replace("`1", "";

                    }
               return ptype;
          }
          public dynamic ParseEnum(Type type)
          {
               var list = new List();
               foreach (var val in Enum.GetValues(type))
               {
                    list.Add(string.Format("{0} ({1})",Enum.Parse(type,val.ToString()).ToString(),((int)val).ToString() ));
               }
               return list;
          }

          public dynamic ParseController(Type type)
          {
               var methods = new List();
               string input = "none";
               string output = "none";
               bool typesSet = false;
               bool requiresSSL =
                    type.CustomAttributes.Any(x => x.AttributeType == typeof(RequireSSLAttribute));
               bool unAuthenticated =
                    type.CustomAttributes.Any(
                         x => x.AttributeType == typeof (UnauthenticatedAttribute));
               foreach (
                    var method in
                         type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
                    )
               {
                    methods.Add(method.Name);
                    if (!typesSet)
                    {
                         var parms = method.GetParameters();
                         if (parms.Any())
                              input = parms.First().ParameterType.Name;
                         var returnType = method.ReturnType;
                         output = returnType.Name;
                         typesSet = true;
                    }
               }
               return new
                    {
                         Methods = methods,
                         Input = input,
                         Output = output,
                         RequiresSSL = requiresSSL,
                         RequiresAuthentication = !unAuthenticated,
                    };

          }
     }

Advertisements
Tagged ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: