1 /** 2 * Authors: Szabo Bogdan <szabobogdan@yahoo.com> 3 * Date: 9 13, 2015 4 * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 5 * Copyright: Public Domain 6 */ 7 module openapi.composites; 8 9 import std.stdio, std.traits, std.exception, std.conv; 10 import vibe.http.server; 11 import vibe.http.router; 12 import openapi.definitions; 13 import openapi.exceptions; 14 15 alias VibeHandler = void function(HTTPServerRequest, HTTPServerResponse); 16 17 struct swaggerPath { 18 string path; 19 OperationsType type; 20 21 @property string vibePath() { 22 import std.array : replace; 23 return path.replace("{", ":").replace("}", ""); 24 } 25 } 26 27 struct ErrorOutput { 28 string[] errors; 29 30 this(Throwable e) { 31 errors ~= e.msg; 32 } 33 } 34 35 template Alias(alias S) 36 { 37 alias Alias = S; 38 } 39 40 private string alignString(string path, int max = 30) { 41 if(path.length < max) { 42 foreach(i; path.length..max) { 43 path ~= ' '; 44 } 45 } 46 47 return path; 48 } 49 50 VibeHandler[string][OperationsType] findComposites(BaseModule...)() { 51 import std.uni: toUpper; 52 53 VibeHandler[string][OperationsType] list; 54 55 static if(__traits(allMembers, BaseModule).length > 0) { 56 pragma(msg, "\nMap OpenApi Paths:"); 57 58 foreach(symbol_name; __traits(allMembers, BaseModule)) 59 { 60 static if(symbol_name.length < 12 || symbol_name[3..12] != "TypeInfo_") { 61 static if(__traits(compiles, typeof(Alias!(__traits(getMember, BaseModule, symbol_name))))) { 62 alias symbol = Alias!(__traits(getMember, BaseModule, symbol_name)); 63 static if(__traits(compiles, typeof(symbol)) && isSomeFunction!symbol) { 64 foreach(attr; __traits(getAttributes, symbol)) { 65 static if(attr.stringof.length > 12 && attr.stringof[0..12] == "swaggerPath(") { 66 pragma(msg, alignString(attr.type, 8), alignString(attr.path), " => ", symbol_name); 67 list[attr.vibePath][attr.type] = &symbol; 68 } 69 } 70 } 71 } 72 } 73 } 74 pragma(msg, "\n"); 75 } 76 77 return list; 78 } 79 80 auto validation(VibeHandler handler, OpenApi definitions) { 81 import openapi.validation; 82 83 void doValidation(HTTPServerRequest req, HTTPServerResponse res) { 84 writeln(req.method.to!string ~ " " ~ req.path); 85 try { 86 req.validate!(ParameterIn.path)(definitions); 87 req.validate!(ParameterIn.query)(definitions); 88 req.validate!(ParameterIn.header)(definitions); 89 req.validateBody(definitions); 90 91 handler(req, res); 92 } catch(OpenApiValidationException e) { 93 res.writeJsonBody(ErrorOutput(e), HTTPStatus.badRequest); 94 debug { 95 writeln(e); 96 } 97 } catch(OpenApiParameterException e) { 98 res.writeJsonBody(ErrorOutput(e), HTTPStatus.badRequest); 99 debug { 100 writeln(e); 101 } 102 } catch(OpenApiNotFoundException e) { 103 res.writeJsonBody(ErrorOutput(e), HTTPStatus.notFound); 104 debug { 105 writeln(e); 106 } 107 } catch(Throwable e) { 108 res.writeJsonBody(ErrorOutput(e), HTTPStatus.internalServerError); 109 110 debug { 111 writeln(e); 112 res.writeJsonBody(ErrorOutput(e)); 113 } else { 114 res.writeBody("{ errors: [\"Internal server error\"] }"); 115 } 116 } 117 } 118 119 return &doValidation; 120 } 121 122 void register(BaseModule...)(URLRouter router) { 123 enum auto handlers = findComposites!BaseModule; 124 125 foreach(path, methods; handlers) { 126 with (router.route(path)) { 127 foreach(method, handler; methods) { 128 switch(method) { 129 case OperationsType.get: 130 get(handler); 131 break; 132 case OperationsType.put: 133 put(handler); 134 break; 135 case OperationsType.post: 136 post(handler); 137 break; 138 case OperationsType.delete_: 139 delete_(handler); 140 break; 141 case OperationsType.patch: 142 patch(handler); 143 break; 144 default: 145 enforce("method `" ~ method ~ "` not found"); 146 } 147 } 148 } 149 } 150 } 151 152 void register(BaseModule...)(URLRouter router, OpenApi definitions) { 153 const auto handlers = findComposites!BaseModule; 154 155 auto basePath = definitions.basePath == "/" ? "" : definitions.basePath; 156 157 foreach(path, methods; handlers) { 158 with (router.route(basePath ~ path)) { 159 foreach(method, handler; methods) { 160 switch(method) { 161 case OperationsType.get: 162 get(handler.validation(definitions)); 163 break; 164 case OperationsType.put: 165 put(handler.validation(definitions)); 166 break; 167 case OperationsType.post: 168 post(handler.validation(definitions)); 169 break; 170 case OperationsType.delete_: 171 delete_(handler.validation(definitions)); 172 break; 173 case OperationsType.patch: 174 patch(handler.validation(definitions)); 175 break; 176 case OperationsType.options: 177 match(HTTPMethod.OPTIONS, handler.validation(definitions)); 178 break; 179 default: 180 enforce("method `" ~ method ~ "` not found"); 181 } 182 } 183 } 184 } 185 }