private static void AddQueryOperation(NSwag.OpenApiOperation operation, CommandEndpointRegistration registration, bool patternParametersOnly = false) { foreach (var property in registration.RequestType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { // translate commandType properties to many OpenApiParameters if (!property.CanWrite || !property.CanRead) { continue; } var type = JsonObjectType.String; if (property.PropertyType == typeof(int) || property.PropertyType == typeof(short)) { type = JsonObjectType.Integer; } else if (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(float) || property.PropertyType == typeof(long)) { type = JsonObjectType.Number; } else if (property.PropertyType == typeof(bool)) { type = JsonObjectType.Boolean; } else if (property.PropertyType == typeof(object)) { type = JsonObjectType.Object; // TODO: does not work for child objects } if (!patternParametersOnly) { // always add parameter operation.Parameters.Add(new OpenApiParameter { //Description = "request model", // TODO: define a description per param (dictionary) and use it here Kind = registration.Pattern.Contains($"{{{property.Name}", StringComparison.OrdinalIgnoreCase) ? OpenApiParameterKind.Path : OpenApiParameterKind.Query, // query routes are not really supported! Name = property.Name.Camelize(), Type = type, }); } else { // only add parameter if it appears in the route pattern if (registration.Pattern.Contains($"{{{property.Name}", StringComparison.OrdinalIgnoreCase)) { operation.Parameters.Add(new OpenApiParameter { //Description = "request model", // TODO: define a description per param (dictionary) and use it here Kind = OpenApiParameterKind.Path, Name = property.Name.Camelize(), Type = type, }); } } } }
private static void AddBodyOperation(NSwag.OpenApiOperation operation, CommandEndpointRegistration registration, DocumentProcessorContext context) { operation.Parameters.Add(new OpenApiParameter { Description = registration.OpenApi?.RequestBodyDescription, //registration.RequestType.PrettyName(), //"request model", Kind = OpenApiParameterKind.Body, Name = registration.RequestType.PrettyName(), //"model", Type = JsonObjectType.Object, Schema = EnsureSchema(registration, context), //Example = registration.CommandType != null ? Factory.Create(registration.CommandType) : null //new Commands.Domain.EchoCommand() { Message = "test"}, }); }
private static JsonSchema EnsureSchema(CommandEndpointRegistration registration, DocumentProcessorContext context) { var schemaKey = registration.RequestType.PrettyName(); if (RequestSchemas.ContainsKey(schemaKey)) { return(RequestSchemas[schemaKey]); } else { var schema = context.SchemaGenerator.Generate(registration.RequestType, context.SchemaResolver); RequestSchemas.Add(schemaKey, schema); return(schema); } }
public CommandEndpointRegistration AddRegistration <TRequest>(string pattern, HttpMethod method) { this.registrations ??= new List <CommandEndpointRegistration>(); var registration = new CommandEndpointRegistration { Name = typeof(TRequest).Name, Pattern = pattern, Method = method, RequestType = typeof(TRequest), ResponseType = typeof(TRequest).GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest <>))?.GetGenericArguments()?[0] }; this.registrations.Add(registration); return(registration); }
private static void AddResponseHeaders(NSwag.OpenApiOperation operation, CommandEndpointRegistration registration) { foreach (var response in operation.Responses) { if (registration.RequestType.GetInterfaces().Contains(typeof(ICommand))) { response.Value.Headers.Add("X-CommandId", new OpenApiHeader { Type = JsonObjectType.String }); } else if (registration.RequestType.GetInterfaces().Contains(typeof(IQuery))) { response.Value.Headers.Add("X-QueryId", new OpenApiHeader { Type = JsonObjectType.String }); } } }
private static void AddOperationParameters(NSwag.OpenApiOperation operation, string method, CommandEndpointRegistration registration, DocumentProcessorContext context) { if (registration.RequestType != null) { if (method.SafeEquals("get") || method.SafeEquals("delete")) { AddQueryOperation(operation, registration); } else if (method.SafeEquals("post") || method.SafeEquals("put") || method.SafeEquals("patch") || method.SafeEquals(string.Empty)) { AddQueryOperation(operation, registration, true); AddBodyOperation(operation, registration, context); } else { // TODO: ignore for now, or throw? +log } } }
private static async Task SendRequest( HttpContext context, CommandEndpointRegistration registration, object requestModel, string requestId) { var mediator = context.RequestServices.GetService <IMediator>(); try { var response = await mediator.Send(requestModel, context.RequestAborted).ConfigureAwait(false); if (response is Unit) // Unit is the empty mediatr response { response = null; } registration.Response?.InvokeSuccess(requestModel, response, context); context.Response.StatusCode = (int)registration.Response.OnSuccessStatusCode; context.Response.Headers.Add("Content-Type", registration.OpenApi?.Produces); if (response == null && requestModel is IQuery && !typeof(IEnumerable <>).IsAssignableFrom(registration.ResponseType)) { // query for single resource with no result > 404 context.Response.StatusCode = (int)HttpStatusCode.NotFound; } else if (response != null && registration.Response?.IgnoreResponseBody == false) { await JsonSerializer.SerializeAsync( context.Response.Body, response, response?.GetType() ?? registration.ResponseType, null, context.RequestAborted).ConfigureAwait(false); } } catch (ValidationException ex) // 400 { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; context.Response.Headers.Add("Content-Type", "application/problem+json"); await JsonSerializer.SerializeAsync( context.Response.Body, new ProblemDetails { Status = (int)HttpStatusCode.BadRequest, Title = "A validation error has occurred while executing the request", Type = ex.GetType().Name, Detail = ex.Message, Instance = requestId }, typeof(ProblemDetails), null, context.RequestAborted).ConfigureAwait(false); } catch (Exception ex) // 500 { context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.Headers.Add("Content-Type", "application/problem+json"); await JsonSerializer.SerializeAsync( context.Response.Body, new ProblemDetails { Status = (int)HttpStatusCode.InternalServerError, Title = "An unhandled error has occurred while processing the request", Type = ex.GetType().Name, Detail = ex.Message, Instance = requestId }, typeof(ProblemDetails), null, context.RequestAborted).ConfigureAwait(false); } await context.Response.Body.FlushAsync(context.RequestAborted).ConfigureAwait(false); }