private static bool FieldUsesKind(ServiceInfo service, ServiceFieldInfo field, ServiceTypeKind kind)
        {
            var type = service.GetFieldType(field);

            if (type.Kind == kind)
            {
                return(true);
            }

            var valueType = type;

            while (true)
            {
                valueType = valueType.ValueType;
                if (valueType == null)
                {
                    break;
                }
                if (valueType.Kind == kind)
                {
                    return(true);
                }
            }

            return(false);
        }
Ejemplo n.º 2
0
        private string RenderUriComponent(ServiceFieldInfo field, ServiceInfo service)
        {
            var fieldTypeKind = service.GetFieldType(field).Kind;
            var fieldName     = field.Name;

            switch (fieldTypeKind)
            {
            case ServiceTypeKind.Enum:
                return($"request.{fieldName}");

            case ServiceTypeKind.String:
            case ServiceTypeKind.Bytes:
                return($"encodeURIComponent(request.{fieldName})");

            case ServiceTypeKind.Boolean:
            case ServiceTypeKind.Int32:
            case ServiceTypeKind.Int64:
            case ServiceTypeKind.Decimal:
                return($"request.{fieldName}.toString()");

            case ServiceTypeKind.Double:
                return($"encodeURIComponent(request.{fieldName}.toString())");

            case ServiceTypeKind.Dto:
            case ServiceTypeKind.Error:
            case ServiceTypeKind.Object:
                throw new NotSupportedException("Field type not supported on path/query: " + fieldTypeKind);

            default:
                throw new NotSupportedException("Unknown field type " + fieldTypeKind);
            }
        }
Ejemplo n.º 3
0
        private string RenderJsConversion(ServiceFieldInfo field, ServiceInfo service, string value)
        {
            var fieldTypeKind = service.GetFieldType(field).Kind;

            switch (fieldTypeKind)
            {
            case ServiceTypeKind.Enum:
            case ServiceTypeKind.String:
            case ServiceTypeKind.Bytes:
                return(value);

            case ServiceTypeKind.Boolean:
                return($"parseBoolean({value})");

            case ServiceTypeKind.Int32:
            case ServiceTypeKind.Int64:
                return($"parseInt({value}, 10)");

            case ServiceTypeKind.Decimal:
            case ServiceTypeKind.Double:
                return($"parseFloat({value})");

            case ServiceTypeKind.Dto:
            case ServiceTypeKind.Error:
            case ServiceTypeKind.Object:
                throw new NotSupportedException("Field type not supported on path/query: " + fieldTypeKind);

            default:
                throw new NotSupportedException("Unknown field type " + fieldTypeKind);
            }
        }
Ejemplo n.º 4
0
        private static bool IsValidRequestBodyField(ServiceFieldInfo fieldInfo, ServiceInfo serviceInfo)
        {
            var fieldTypeKind = serviceInfo.GetFieldType(fieldInfo).Kind;

            return(fieldTypeKind == ServiceTypeKind.Object ||
                   fieldTypeKind == ServiceTypeKind.Error ||
                   fieldTypeKind == ServiceTypeKind.Dto ||
                   fieldTypeKind == ServiceTypeKind.Result ||
                   fieldTypeKind == ServiceTypeKind.Array ||
                   fieldTypeKind == ServiceTypeKind.Map);
        }
Ejemplo n.º 5
0
        public void PrimitiveTypes(string name, ServiceTypeKind kind)
        {
            var service = new ServiceInfo(name: "MyApi", members: new[] { new ServiceDtoInfo("MyDto", fields: new[] { new ServiceFieldInfo("myField", name) }) });
            var type    = service.GetFieldType(service.Dtos[0].Fields[0]);

            type.Kind.ShouldBe(kind);
            type.Dto.ShouldBeNull();
            type.Enum.ShouldBeNull();
            type.ValueType.ShouldBeNull();
            type.ToString().ShouldBe(name);
        }
Ejemplo n.º 6
0
        public void EnumType()
        {
            var service = new ServiceInfo(name: "MyApi",
                                          members: new IServiceMemberInfo[] { new ServiceMethodInfo("myMethod", requestFields: new[] { new ServiceFieldInfo("myField", "MyEnum") }), new ServiceEnumInfo("MyEnum") });
            var type = service.GetFieldType(service.Methods[0].RequestFields[0]);

            type.Kind.ShouldBe(ServiceTypeKind.Enum);
            type.Dto.ShouldBeNull();
            type.Enum.ShouldBe(service.Enums[0]);
            type.ValueType.ShouldBeNull();
            type.ToString().ShouldBe("MyEnum");
        }
Ejemplo n.º 7
0
        public void ContainerOfDtoType(string name, ServiceTypeKind kind)
        {
            var service = new ServiceInfo(name: "MyApi",
                                          members: new IServiceMemberInfo[] { new ServiceMethodInfo("myMethod", requestFields: new[] { new ServiceFieldInfo("myField", name) }), new ServiceDtoInfo("MyDto") });
            var type = service.GetFieldType(service.Methods[0].RequestFields[0]);

            type.Kind.ShouldBe(kind);
            type.Dto.ShouldBeNull();
            type.Enum.ShouldBeNull();
            type.ValueType.Dto.ShouldBe(service.Dtos[0]);
            type.ToString().ShouldBe(name);
        }
Ejemplo n.º 8
0
        private static bool IsValidPathOrQueryField(ServiceFieldInfo fieldInfo, ServiceInfo serviceInfo)
        {
            var fieldTypeKind = serviceInfo.GetFieldType(fieldInfo).Kind;

            return(fieldTypeKind == ServiceTypeKind.String ||
                   fieldTypeKind == ServiceTypeKind.Boolean ||
                   fieldTypeKind == ServiceTypeKind.Double ||
                   fieldTypeKind == ServiceTypeKind.Int32 ||
                   fieldTypeKind == ServiceTypeKind.Int64 ||
                   fieldTypeKind == ServiceTypeKind.Decimal ||
                   fieldTypeKind == ServiceTypeKind.Enum);
        }
Ejemplo n.º 9
0
 public static ServiceMemberInfo GetMemberReferencedAtPosition(this ServiceInfo service, Position requestPosition)
 {
     return((
                from field in service.GetDescendants().OfType <ServiceFieldInfo>()
                let part = field.GetPart(ServicePartKind.TypeName)
                           where part != null && requestPosition >= part.Position && requestPosition < part.EndPosition
                           let type = service.GetFieldType(field)
                                      where type != null
                                      let name = type.GetMemberTypeName()
                                                 where name != null
                                                 select service.FindMember(name)).FirstOrDefault());
 }
Ejemplo n.º 10
0
        private IEnumerable <HttpResponseInfo> GetValidResponses(ServiceInfo serviceInfo, HttpStatusCode?statusCode, IReadOnlyList <HttpNormalFieldInfo> responseNormalFields, IReadOnlyList <HttpBodyFieldInfo> responseBodyFields)
        {
            foreach (var responseBodyField in responseBodyFields)
            {
                // use the status code on the field or the default: OK or NoContent
                HttpStatusCode bodyStatusCode;
                bool           isBoolean = serviceInfo.GetFieldType(responseBodyField.ServiceField).Kind == ServiceTypeKind.Boolean;
                if (responseBodyField.StatusCode != null)
                {
                    bodyStatusCode = responseBodyField.StatusCode.Value;
                }
                else
                {
                    bodyStatusCode = isBoolean ? HttpStatusCode.NoContent : HttpStatusCode.OK;
                }

                // 204 and 304 don't support content
                if (IsNoContentStatusCode(bodyStatusCode) && !isBoolean)
                {
                    throw new ServiceDefinitionException($"A body field with HTTP status code {(int) bodyStatusCode} must be Boolean.", responseBodyField.ServiceField.Position);
                }

                yield return(new HttpResponseInfo(
                                 statusCode: bodyStatusCode,
                                 bodyField: responseBodyField));
            }

            // if the DTO has a status code, or there are any normal fields, or there are no body fields, the DTO must represent a status code
            HttpStatusCode?responseStatusCode = null;

            if (statusCode != null)
            {
                responseStatusCode = statusCode;
            }
            else if (responseNormalFields.Count != 0 || responseBodyFields.Count == 0)
            {
                responseStatusCode = HttpStatusCode.OK;
            }
            if (responseStatusCode != null)
            {
                // 204 and 304 don't support content
                if (IsNoContentStatusCode(responseStatusCode) && responseNormalFields.Count != 0)
                {
                    throw new ServiceDefinitionException($"HTTP status code {(int) responseStatusCode} does not support normal fields.", responseNormalFields[0].ServiceField.Position);
                }

                yield return(new HttpResponseInfo(
                                 statusCode: responseStatusCode.Value,
                                 normalFields: responseNormalFields));
            }
        }
Ejemplo n.º 11
0
 private void WriteDto(CodeWriter code, ServiceDtoInfo dtoInfo, ServiceInfo service)
 {
     code.WriteLine();
     WriteJsDoc(code, dtoInfo);
     using (code.Block($"export interface I{CodeGenUtility.Capitalize(dtoInfo.Name)} {{", "}"))
     {
         foreach (var fieldInfo in dtoInfo.Fields)
         {
             code.WriteLineSkipOnce();
             WriteJsDoc(code, fieldInfo);
             code.WriteLine($"{fieldInfo.Name}?: {RenderFieldType(service.GetFieldType(fieldInfo))};");
         }
     }
 }
Ejemplo n.º 12
0
        private static SwaggerParameter CreateSwaggerParameter(ServiceInfo service, ServiceFieldInfo fieldInfo, string inKind, string name)
        {
            var parameterObject = GetTypeSchema <SwaggerParameter>(service.GetFieldType(fieldInfo));

            parameterObject.In   = inKind;
            parameterObject.Name = name ?? fieldInfo.Name;
            if (parameterObject.Name != fieldInfo.Name)
            {
                parameterObject.Identifier = fieldInfo.Name;
            }
            parameterObject.Description = GetSummaryOrNull(fieldInfo);
            parameterObject.Required    = inKind == SwaggerParameterKind.Path ? true : default(bool?);
            parameterObject.Obsolete    = GetObsoleteOrNull(fieldInfo);
            return(parameterObject);
        }
Ejemplo n.º 13
0
        private static bool IsValidSimpleField(ServiceFieldInfo fieldInfo, ServiceInfo serviceInfo)
        {
            var fieldType     = serviceInfo.GetFieldType(fieldInfo);
            var fieldTypeKind = fieldType?.Kind;

            if (fieldTypeKind == null)
            {
                return(false);
            }

            if (fieldTypeKind == ServiceTypeKind.Array)
            {
                fieldTypeKind = fieldType.ValueType.Kind;
            }

            return(fieldTypeKind == ServiceTypeKind.String ||
                   fieldTypeKind == ServiceTypeKind.Boolean ||
                   fieldTypeKind == ServiceTypeKind.Double ||
                   fieldTypeKind == ServiceTypeKind.Int32 ||
                   fieldTypeKind == ServiceTypeKind.Int64 ||
                   fieldTypeKind == ServiceTypeKind.Decimal ||
                   fieldTypeKind == ServiceTypeKind.Enum);
        }
Ejemplo n.º 14
0
        private static SwaggerSchema GetDtoSchema(ServiceInfo serviceInfo, ServiceDtoInfo dtoInfo)
        {
            var propertiesObject = new OurDictionary <string, SwaggerSchema>();

            foreach (var fieldInfo in dtoInfo.Fields)
            {
                SwaggerSchema propertyObject = GetTypeSchema <SwaggerSchema>(serviceInfo.GetFieldType(fieldInfo));
                if (propertyObject.Ref == null)
                {
                    propertyObject.Description = GetSummaryOrNull(fieldInfo);
                    propertyObject.Obsolete    = GetObsoleteOrNull(fieldInfo);
                }
                propertiesObject[fieldInfo.Name] = propertyObject;
            }

            return(new SwaggerSchema
            {
                Type = SwaggerSchemaType.Object,
                Description = GetSummaryOrNull(dtoInfo),
                Properties = propertiesObject,
                Obsolete = GetObsoleteOrNull(dtoInfo),
                Remarks = GetRemarksOrNull(dtoInfo),
            });
        }
Ejemplo n.º 15
0
        private static void AddMethodToPaths(IDictionary <string, SwaggerOperations> paths, ServiceInfo service, HttpMethodInfo httpMethodInfo)
        {
            var methodInfo = httpMethodInfo.ServiceMethod;

            SwaggerOperations operations;

            if (!paths.TryGetValue(httpMethodInfo.Path, out operations))
            {
                paths[httpMethodInfo.Path] = operations = new SwaggerOperations();
            }

            var operation = new SwaggerOperation
            {
                Summary     = GetSummaryOrNull(methodInfo),
                Description = GetRemarksOrNull(methodInfo),
                OperationId = methodInfo.Name,
                Deprecated  = GetObsoleteOrNull(methodInfo),
            };

            if (httpMethodInfo.RequestNormalFields.Count != 0 || httpMethodInfo.RequestBodyField != null)
            {
                operation.Consumes = new[] { "application/json" }
            }
            ;
            if (httpMethodInfo.ValidResponses.Any(x => (x.NormalFields != null && x.NormalFields.Count != 0) || (x.BodyField != null && service.GetFieldType(x.BodyField.ServiceField).Kind != ServiceTypeKind.Boolean)))
            {
                operation.Produces = new[] { "application/json" }
            }
            ;

            var parameters = new List <SwaggerParameter>();

            foreach (var httpPathInfo in httpMethodInfo.PathFields)
            {
                parameters.Add(CreateSwaggerParameter(service, httpPathInfo.ServiceField, SwaggerParameterKind.Path, httpPathInfo.Name));
            }

            foreach (var httpQueryInfo in httpMethodInfo.QueryFields)
            {
                parameters.Add(CreateSwaggerParameter(service, httpQueryInfo.ServiceField, SwaggerParameterKind.Query, httpQueryInfo.Name));
            }

            foreach (var httpHeaderInfo in httpMethodInfo.RequestHeaderFields)
            {
                parameters.Add(CreateSwaggerParameter(service, httpHeaderInfo.ServiceField, SwaggerParameterKind.Header, httpHeaderInfo.Name));
            }

            var requestBodyFieldType = httpMethodInfo.RequestBodyField == null ? null : service.GetFieldType(httpMethodInfo.RequestBodyField.ServiceField);

            if (requestBodyFieldType != null && requestBodyFieldType.Kind != ServiceTypeKind.Boolean)
            {
                parameters.Add(CreateSwaggerRequestBodyParameter(requestBodyFieldType, "request", httpMethodInfo.RequestBodyField.ServiceField.Summary));
            }
            else if (httpMethodInfo.RequestNormalFields.Count != 0)
            {
                parameters.Add(CreateSwaggerRequestBodyParameter(TryCreateMethodRequestBodyType(httpMethodInfo), "request"));
            }

            if (parameters.Count != 0)
            {
                operation.Parameters = parameters;
            }

            var responses = new OurDictionary <string, SwaggerResponse>();

            foreach (var validResponse in httpMethodInfo.ValidResponses)
            {
                string statusCodeString = ((int)validResponse.StatusCode).ToString(CultureInfo.InvariantCulture);

                var bodyField     = validResponse.BodyField;
                var bodyFieldType = bodyField == null ? null : service.GetFieldType(bodyField.ServiceField);
                if (bodyField != null)
                {
                    responses[statusCodeString] = CreateSwaggerResponse(bodyFieldType, bodyField.ServiceField.Name, bodyField.ServiceField.Summary);
                }
                else if (validResponse.NormalFields != null && validResponse.NormalFields.Count != 0)
                {
                    responses[statusCodeString] = CreateSwaggerResponse(TryCreateMethodResponseBodyType(httpMethodInfo, validResponse));
                }
                else
                {
                    responses[statusCodeString] = CreateSwaggerResponse();
                }
            }

            operation.Responses = responses;

            string httpMethod = httpMethodInfo.Method.ToString().ToLowerInvariant();

            switch (httpMethod)
            {
            case "get":
                operations.Get = operation;
                break;

            case "post":
                operations.Post = operation;
                break;

            case "put":
                operations.Put = operation;
                break;

            case "delete":
                operations.Delete = operation;
                break;

            case "options":
                operations.Options = operation;
                break;

            case "head":
                operations.Head = operation;
                break;

            case "patch":
                operations.Patch = operation;
                break;

            default:
                throw new InvalidOperationException("Unexpected HTTP method: " + httpMethod);
            }
        }
        /// <summary>
        /// Generates the JavaScript/TypeScript output.
        /// </summary>
        public override CodeGenOutput GenerateOutput(ServiceInfo service)
        {
            var httpServiceInfo = HttpServiceInfo.Create(service);

            var moduleName     = ModuleName ?? service.Name;
            var capModuleName  = CodeGenUtility.Capitalize(moduleName);
            var typesFileName  = Uncapitalize(moduleName) + "Types" + (TypeScript ? ".ts" : ".js");
            var clientFileName = Uncapitalize(moduleName) + (TypeScript ? ".ts" : ".js");
            var serverFileName = Uncapitalize(moduleName) + "Server" + (TypeScript ? ".ts" : ".js");

            var namedTexts = new List <CodeGenFile>();
            var typeNames  = new List <string>();

            if (TypeScript)
            {
                namedTexts.Add(CreateFile(typesFileName, code =>
                {
                    WriteFileHeader(code);

                    var allFields = service.Dtos.SelectMany(x => x.Fields).Concat(service.Methods.SelectMany(x => x.RequestFields.Concat(x.ResponseFields))).ToList();

                    code.WriteLine();
                    var facilityImports = new List <string>();
                    if (httpServiceInfo.Methods.Any() || allFields.Any(x => FieldUsesKind(service, x, ServiceTypeKind.Result)))
                    {
                        facilityImports.Add("IServiceResult");
                    }
                    if (allFields.Any(x => FieldUsesKind(service, x, ServiceTypeKind.Error)))
                    {
                        facilityImports.Add("IServiceError");
                    }
                    WriteImports(code, facilityImports, "facility-core");

                    code.WriteLine();
                    WriteJsDoc(code, service);
                    typeNames.Add($"I{capModuleName}");
                    using (code.Block($"export interface I{capModuleName} {{", "}"))
                    {
                        foreach (var httpMethodInfo in httpServiceInfo.Methods)
                        {
                            var methodName    = httpMethodInfo.ServiceMethod.Name;
                            var capMethodName = CodeGenUtility.Capitalize(methodName);
                            code.WriteLineSkipOnce();
                            WriteJsDoc(code, httpMethodInfo.ServiceMethod);
                            code.WriteLine($"{methodName}(request: I{capMethodName}Request): Promise<IServiceResult<I{capMethodName}Response>>;");
                        }
                    }

                    foreach (var methodInfo in service.Methods)
                    {
                        var requestDtoName = $"{CodeGenUtility.Capitalize(methodInfo.Name)}Request";
                        typeNames.Add($"I{requestDtoName}");
                        WriteDto(code, new ServiceDtoInfo(
                                     name: requestDtoName,
                                     fields: methodInfo.RequestFields,
                                     summary: $"Request for {CodeGenUtility.Capitalize(methodInfo.Name)}."), service);

                        var responseDtoName = $"{CodeGenUtility.Capitalize(methodInfo.Name)}Response";
                        typeNames.Add($"I{responseDtoName}");
                        WriteDto(code, new ServiceDtoInfo(
                                     name: responseDtoName,
                                     fields: methodInfo.ResponseFields,
                                     summary: $"Response for {CodeGenUtility.Capitalize(methodInfo.Name)}."), service);
                    }

                    foreach (var dtoInfo in service.Dtos)
                    {
                        typeNames.Add($"I{dtoInfo.Name}");
                        WriteDto(code, dtoInfo, service);
                    }
                    code.WriteLine();
                }));
            }

            namedTexts.Add(CreateFile(clientFileName, code =>
            {
                WriteFileHeader(code);

                if (!TypeScript)
                {
                    code.WriteLine("'use strict';");
                }

                code.WriteLine();
                var facilityImports = new List <string> {
                    "HttpClientUtility"
                };
                if (TypeScript)
                {
                    if (httpServiceInfo.Methods.Any())
                    {
                        facilityImports.Add("IServiceResult");
                    }
                    facilityImports.Add("IHttpClientOptions");
                }
                WriteImports(code, facilityImports, "facility-core");

                if (TypeScript)
                {
                    WriteImports(code, typeNames, $"./{Uncapitalize(moduleName)}Types");
                    code.WriteLine($"export * from './{Uncapitalize(moduleName)}Types';");
                }

                code.WriteLine();
                WriteJsDoc(code, $"Provides access to {capModuleName} over HTTP via fetch.");
                using (code.Block("export function createHttpClient({ fetch, baseUri }" + IfTypeScript(": IHttpClientOptions") + ")" + IfTypeScript($": I{capModuleName}") + " {", "}"))
                    code.WriteLine($"return new {capModuleName}HttpClient(fetch, baseUri);");

                code.WriteLine();
                code.WriteLine("const { fetchResponse, createResponseError, createRequiredRequestFieldError } = HttpClientUtility;");
                if (TypeScript)
                {
                    code.WriteLine("type IFetch = HttpClientUtility.IFetch;");
                    code.WriteLine("type IFetchRequest = HttpClientUtility.IFetchRequest;");
                }

                code.WriteLine();
                using (code.Block($"class {capModuleName}HttpClient" + IfTypeScript($" implements I{capModuleName}") + " {", "}"))
                {
                    using (code.Block("constructor(fetch" + IfTypeScript(": IFetch") + ", baseUri" + IfTypeScript("?: string") + ") {", "}"))
                    {
                        using (code.Block("if (typeof fetch !== 'function') {", "}"))
                            code.WriteLine("throw new TypeError('fetch must be a function.');");
                        using (code.Block("if (typeof baseUri === 'undefined') {", "}"))
                            code.WriteLine($"baseUri = '{httpServiceInfo.Url ?? ""}';");
                        using (code.Block(@"if (/[^\/]$/.test(baseUri)) {", "}"))
                            code.WriteLine("baseUri += '/';");

                        code.WriteLine("this._fetch = fetch;");
                        code.WriteLine("this._baseUri = baseUri;");
                    }

                    foreach (var httpMethodInfo in httpServiceInfo.Methods)
                    {
                        var methodName    = httpMethodInfo.ServiceMethod.Name;
                        var capMethodName = CodeGenUtility.Capitalize(methodName);

                        code.WriteLine();
                        WriteJsDoc(code, httpMethodInfo.ServiceMethod);
                        using (code.Block(IfTypeScript("public ") + $"{methodName}(request" + IfTypeScript($": I{capMethodName}Request") + ")" + IfTypeScript($": Promise<IServiceResult<I{capMethodName}Response>>") + " {", "}"))
                        {
                            var hasPathFields = httpMethodInfo.PathFields.Count != 0;
                            var jsUriDelim    = hasPathFields ? "`" : "'";
                            var jsUri         = jsUriDelim + httpMethodInfo.Path.Substring(1) + jsUriDelim;
                            if (hasPathFields)
                            {
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    code.WriteLine($"const uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)} = request.{httpPathField.ServiceField.Name} != null && {RenderUriComponent(httpPathField.ServiceField, service)};");
                                    using (code.Block($"if (!uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}) {{", "}"))
                                        code.WriteLine($"return Promise.resolve(createRequiredRequestFieldError('{httpPathField.ServiceField.Name}'));");
                                }
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    jsUri = jsUri.Replace("{" + httpPathField.Name + "}", $"${{uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}}}");
                                }
                            }

                            var hasQueryFields = httpMethodInfo.QueryFields.Count != 0;
                            code.WriteLine((hasQueryFields ? "let" : "const") + $" uri = {jsUri};");
                            if (hasQueryFields)
                            {
                                code.WriteLine("const query" + IfTypeScript(": string[]") + " = [];");
                                foreach (var httpQueryField in httpMethodInfo.QueryFields)
                                {
                                    code.WriteLine($"request.{httpQueryField.ServiceField.Name} == null || query.push('{httpQueryField.Name}=' + {RenderUriComponent(httpQueryField.ServiceField, service)});");
                                }
                                using (code.Block("if (query.length) {", "}"))
                                    code.WriteLine("uri = uri + '?' + query.join('&');");
                            }

                            using (code.Block("const fetchRequest" + IfTypeScript(": IFetchRequest") + " = {", "};"))
                            {
                                if (httpMethodInfo.RequestBodyField == null && httpMethodInfo.RequestNormalFields.Count == 0)
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}',");
                                    if (httpMethodInfo.RequestHeaderFields.Count != 0)
                                    {
                                        code.WriteLine("headers: {},");
                                    }
                                }
                                else
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}',");
                                    code.WriteLine("headers: { 'Content-Type': 'application/json' },");

                                    if (httpMethodInfo.RequestBodyField != null)
                                    {
                                        code.WriteLine($"body: JSON.stringify(request.{httpMethodInfo.RequestBodyField.ServiceField.Name})");
                                    }
                                    else if (httpMethodInfo.ServiceMethod.RequestFields.Count == httpMethodInfo.RequestNormalFields.Count)
                                    {
                                        code.WriteLine("body: JSON.stringify(request)");
                                    }
                                    else
                                    {
                                        using (code.Block("body: JSON.stringify({", "})"))
                                        {
                                            for (var httpFieldIndex = 0; httpFieldIndex < httpMethodInfo.RequestNormalFields.Count; httpFieldIndex++)
                                            {
                                                var httpFieldInfo = httpMethodInfo.RequestNormalFields[httpFieldIndex];
                                                var isLastField   = httpFieldIndex == httpMethodInfo.RequestNormalFields.Count - 1;
                                                var fieldName     = httpFieldInfo.ServiceField.Name;
                                                code.WriteLine(fieldName + ": request." + fieldName + (isLastField ? "" : ","));
                                            }
                                        }
                                    }
                                }
                            }

                            if (httpMethodInfo.RequestHeaderFields.Count != 0)
                            {
                                foreach (var httpHeaderField in httpMethodInfo.RequestHeaderFields)
                                {
                                    using (code.Block($"if (request.{httpHeaderField.ServiceField.Name} != null) {{", "}"))
                                        code.WriteLine($"fetchRequest.headers['{httpHeaderField.Name}'] = request.{httpHeaderField.ServiceField.Name};");
                                }
                            }

                            code.WriteLine("return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest)");
                            using (code.Indent())
                                using (code.Block(".then(result => {", "});"))
                                {
                                    code.WriteLine("const status = result.response.status;");
                                    code.WriteLine("let value" + IfTypeScript($": I{capMethodName}Response | null") + " = null;");
                                    using (code.Block("if (result.json) {", "}"))
                                    {
                                        var validResponses = httpMethodInfo.ValidResponses;
                                        var elsePrefix     = "";
                                        foreach (var validResponse in validResponses)
                                        {
                                            var statusCodeAsString = ((int)validResponse.StatusCode).ToString(CultureInfo.InvariantCulture);
                                            code.WriteLine($"{elsePrefix}if (status === {statusCodeAsString}) {{");
                                            elsePrefix = "else ";

                                            using (code.Indent())
                                            {
                                                var bodyField = validResponse.BodyField;
                                                if (bodyField != null)
                                                {
                                                    var responseBodyFieldName = bodyField.ServiceField.Name;

                                                    var bodyFieldType = service.GetFieldType(bodyField.ServiceField) !;
                                                    if (bodyFieldType.Kind == ServiceTypeKind.Boolean)
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: true }};");
                                                    }
                                                    else
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: result.json }};");
                                                    }
                                                }
                                                else
                                                {
                                                    if (validResponse.NormalFields !.Count == 0)
                                                    {
                                                        code.WriteLine("value = {};");
                                                    }
Ejemplo n.º 17
0
        /// <summary>
        /// Generates Swagger (OpenAPI 2.0) for a service definition.
        /// </summary>
        public SwaggerService GenerateSwaggerService(ServiceInfo service)
        {
            var httpServiceInfo = HttpServiceInfo.Create(service);

            var swaggerService = new SwaggerService
            {
                Swagger = SwaggerUtility.SwaggerVersion,
                Info    = new SwaggerInfo
                {
                    Identifier  = service.Name,
                    Title       = GetSummaryOrNull(service) ?? service.Name,
                    Description = GetRemarksOrNull(service),
                    Version     = service.TryGetAttribute("info")?.TryGetParameterValue("version") ?? "0.0.0",
                    CodeGen     = CodeGenUtility.GetCodeGenComment(GeneratorName ?? ""),
                },
            };

            var defaultBaseUri = httpServiceInfo.Url;

            if (defaultBaseUri != null)
            {
                var baseUri = new Uri(defaultBaseUri);
                swaggerService.Host    = baseUri.Host;
                swaggerService.Schemes = new[] { baseUri.Scheme };

                string basePath = baseUri.PathAndQuery;
                if (!string.IsNullOrEmpty(basePath) && basePath != "/")
                {
                    swaggerService.BasePath = baseUri.PathAndQuery;
                }
            }

            var paths = new OurDictionary <string, SwaggerOperations>();

            foreach (var httpMethodInfo in httpServiceInfo.Methods)
            {
                AddMethodToPaths(paths, service, httpMethodInfo);
            }
            swaggerService.Paths = paths;

            var dtoInfos = new OurDictionary <string, ServiceDtoInfo>();

            foreach (var httpMethodInfo in httpServiceInfo.Methods)
            {
                if (httpMethodInfo.RequestBodyField != null)
                {
                    AddDtos(dtoInfos, GetDtosForType(service.GetFieldType(httpMethodInfo.RequestBodyField.ServiceField) !));
                }

                AddDto(dtoInfos, TryCreateMethodRequestBodyType(httpMethodInfo)?.Dto);

                foreach (var httpResponseInfo in httpMethodInfo.ValidResponses)
                {
                    if (httpResponseInfo.BodyField != null)
                    {
                        AddDtos(dtoInfos, GetDtosForType(service.GetFieldType(httpResponseInfo.BodyField.ServiceField) !));
                    }

                    AddDto(dtoInfos, TryCreateMethodResponseBodyType(httpMethodInfo, httpResponseInfo)?.Dto);
                }
            }

            while (true)
            {
                var dtoCount = dtoInfos.Count;
                foreach (var field in dtoInfos.Values.SelectMany(x => x.Fields).ToList())
                {
                    AddDtos(dtoInfos, GetDtosForType(service.GetFieldType(field) !));
                }
                if (dtoCount == dtoInfos.Count)
                {
                    break;
                }
            }

            var definitions = new OurDictionary <string, SwaggerSchema>();

            foreach (var dtoInfo in dtoInfos.Values)
            {
                definitions[dtoInfo.Name] = GetDtoSchema(service, dtoInfo);
            }
            swaggerService.Definitions = definitions.Count == 0 ? null : definitions;

            return(swaggerService);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Generates the C# output.
        /// </summary>
        protected override CodeGenOutput GenerateOutputCore(ServiceInfo service)
        {
            var httpServiceInfo = HttpServiceInfo.Create(service);

            string moduleName     = ModuleName ?? service.Name;
            string capModuleName  = CodeGenUtility.Capitalize(moduleName);
            string typesFileName  = Uncapitalize(moduleName) + "Types" + (TypeScript ? ".ts" : ".js");
            string clientFileName = Uncapitalize(moduleName) + (TypeScript ? ".ts" : ".js");
            string serverFileName = Uncapitalize(moduleName) + "Server" + (TypeScript ? ".ts" : ".js");

            var namedTexts = new List <CodeGenFile>();
            var typeNames  = new List <string>();

            if (TypeScript)
            {
                namedTexts.Add(CreateFile(typesFileName, code =>
                {
                    code.WriteLine($"// DO NOT EDIT: generated by {GeneratorName}");

                    code.WriteLine();
                    code.WriteLine("import { IServiceResult, IServiceError } from 'facility-core';");

                    code.WriteLine();
                    WriteJsDoc(code, service);
                    typeNames.Add($"I{capModuleName}");
                    using (code.Block($"export interface I{capModuleName} {{", "}"))
                    {
                        foreach (var httpMethodInfo in httpServiceInfo.Methods)
                        {
                            string methodName    = httpMethodInfo.ServiceMethod.Name;
                            string capMethodName = CodeGenUtility.Capitalize(methodName);
                            code.WriteLineSkipOnce();
                            WriteJsDoc(code, httpMethodInfo.ServiceMethod);
                            code.WriteLine($"{methodName}(request: I{capMethodName}Request): Promise<IServiceResult<I{capMethodName}Response>>;");
                        }
                    }

                    foreach (var methodInfo in service.Methods)
                    {
                        var requestDtoName = $"{CodeGenUtility.Capitalize(methodInfo.Name)}Request";
                        typeNames.Add($"I{requestDtoName}");
                        WriteDto(code, new ServiceDtoInfo(
                                     name: requestDtoName,
                                     fields: methodInfo.RequestFields,
                                     summary: $"Request for {CodeGenUtility.Capitalize(methodInfo.Name)}."), service);

                        var responseDtoName = $"{CodeGenUtility.Capitalize(methodInfo.Name)}Response";
                        typeNames.Add($"I{responseDtoName}");
                        WriteDto(code, new ServiceDtoInfo(
                                     name: responseDtoName,
                                     fields: methodInfo.ResponseFields,
                                     summary: $"Response for {CodeGenUtility.Capitalize(methodInfo.Name)}."), service);
                    }

                    foreach (var dtoInfo in service.Dtos)
                    {
                        typeNames.Add($"I{dtoInfo.Name}");
                        WriteDto(code, dtoInfo, service);
                    }
                    code.WriteLine();
                }));
            }


            namedTexts.Add(CreateFile(clientFileName, code =>
            {
                code.WriteLine($"// DO NOT EDIT: generated by {GeneratorName}");

                if (!TypeScript)
                {
                    code.WriteLine("'use strict';");
                }

                code.WriteLine();
                code.WriteLine("import { HttpClientUtility" + IfTypeScript(", IServiceResult, IServiceError, IHttpClientOptions") + " } from 'facility-core';");
                if (TypeScript)
                {
                    code.WriteLine($"import {{ {string.Join(", ", typeNames)} }} from './{Uncapitalize(moduleName)}Types';");
                    code.WriteLine($"export * from './{Uncapitalize(moduleName)}Types';");
                }

                code.WriteLine();
                WriteJsDoc(code, $"Provides access to {capModuleName} over HTTP via fetch.");
                using (code.Block("export function createHttpClient({ fetch, baseUri }" + IfTypeScript(": IHttpClientOptions") + ")" + IfTypeScript($": I{capModuleName}") + " {", "}"))
                    code.WriteLine($"return new {capModuleName}HttpClient(fetch, baseUri);");

                code.WriteLine();
                code.WriteLine("const { fetchResponse, createResponseError, createRequiredRequestFieldError } = HttpClientUtility;");
                if (TypeScript)
                {
                    code.WriteLine("type IFetch = HttpClientUtility.IFetch;");
                    code.WriteLine("type IFetchRequest = HttpClientUtility.IFetchRequest;");
                }

                code.WriteLine();
                using (code.Block($"class {capModuleName}HttpClient" + IfTypeScript($" implements I{capModuleName}") + " {", "}"))
                {
                    using (code.Block("constructor(fetch" + IfTypeScript(": IFetch") + ", baseUri" + IfTypeScript("?: string") + ") {", "}"))
                    {
                        using (code.Block("if (typeof fetch !== 'function') {", "}"))
                            code.WriteLine("throw new TypeError('fetch must be a function.');");
                        using (code.Block("if (typeof baseUri === 'undefined') {", "}"))
                            code.WriteLine($"baseUri = '{httpServiceInfo.Url ?? ""}';");
                        using (code.Block(@"if (/[^\/]$/.test(baseUri)) {", "}"))
                            code.WriteLine("baseUri += '/';");

                        code.WriteLine("this._fetch = fetch;");
                        code.WriteLine("this._baseUri = baseUri;");
                    }

                    foreach (var httpMethodInfo in httpServiceInfo.Methods)
                    {
                        string methodName    = httpMethodInfo.ServiceMethod.Name;
                        string capMethodName = CodeGenUtility.Capitalize(methodName);

                        code.WriteLine();
                        WriteJsDoc(code, httpMethodInfo.ServiceMethod);
                        using (code.Block(IfTypeScript("public ") + $"{methodName}(request" + IfTypeScript($": I{capMethodName}Request") + ")" + IfTypeScript($": Promise<IServiceResult<I{capMethodName}Response>>") + " {", "}"))
                        {
                            bool hasPathFields = httpMethodInfo.PathFields.Count != 0;
                            string jsUriDelim  = hasPathFields ? "`" : "'";
                            string jsUri       = jsUriDelim + httpMethodInfo.Path.Substring(1) + jsUriDelim;
                            if (hasPathFields)
                            {
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    code.WriteLine($"const uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)} = request.{httpPathField.ServiceField.Name} != null && {RenderUriComponent(httpPathField.ServiceField, service)};");
                                    using (code.Block($"if (!uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}) {{", "}"))
                                        code.WriteLine($"return Promise.resolve(createRequiredRequestFieldError('{httpPathField.ServiceField.Name}'));");
                                }
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    jsUri = jsUri.Replace("{" + httpPathField.Name + "}", $"${{uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}}}");
                                }
                            }

                            bool hasQueryFields = httpMethodInfo.QueryFields.Count != 0;
                            code.WriteLine((hasQueryFields ? "let" : "const") + $" uri = {jsUri};");
                            if (hasQueryFields)
                            {
                                code.WriteLine("const query" + IfTypeScript(": string[]") + " = [];");
                                foreach (var httpQueryField in httpMethodInfo.QueryFields)
                                {
                                    code.WriteLine($"request.{httpQueryField.ServiceField.Name} == null || query.push('{httpQueryField.Name}=' + {RenderUriComponent(httpQueryField.ServiceField, service)});");
                                }
                                using (code.Block("if (query.length) {", "}"))
                                    code.WriteLine("uri = uri + '?' + query.join('&');");
                            }

                            using (code.Block("const fetchRequest" + IfTypeScript(": IFetchRequest") + " = {", "};"))
                            {
                                if (httpMethodInfo.RequestBodyField == null && httpMethodInfo.RequestNormalFields.Count == 0)
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}',");
                                    if (httpMethodInfo.RequestHeaderFields.Count != 0)
                                    {
                                        code.WriteLine("headers: {},");
                                    }
                                }
                                else
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}',");
                                    code.WriteLine("headers: { 'Content-Type': 'application/json' },");

                                    if (httpMethodInfo.RequestBodyField != null)
                                    {
                                        code.WriteLine($"body: JSON.stringify(request.{httpMethodInfo.RequestBodyField.ServiceField.Name})");
                                    }
                                    else if (httpMethodInfo.ServiceMethod.RequestFields.Count == httpMethodInfo.RequestNormalFields.Count)
                                    {
                                        code.WriteLine("body: JSON.stringify(request)");
                                    }
                                    else
                                    {
                                        using (code.Block("body: JSON.stringify({", "})"))
                                        {
                                            for (int httpFieldIndex = 0; httpFieldIndex < httpMethodInfo.RequestNormalFields.Count; httpFieldIndex++)
                                            {
                                                var httpFieldInfo = httpMethodInfo.RequestNormalFields[httpFieldIndex];
                                                bool isLastField  = httpFieldIndex == httpMethodInfo.RequestNormalFields.Count - 1;
                                                string fieldName  = httpFieldInfo.ServiceField.Name;
                                                code.WriteLine(fieldName + ": request." + fieldName + (isLastField ? "" : ","));
                                            }
                                        }
                                    }
                                }
                            }

                            if (httpMethodInfo.RequestHeaderFields.Count != 0)
                            {
                                foreach (var httpHeaderField in httpMethodInfo.RequestHeaderFields)
                                {
                                    using (code.Block($"if (request.{httpHeaderField.ServiceField.Name} != null) {{", "}"))
                                        code.WriteLine($"fetchRequest.headers['{httpHeaderField.Name}'] = request.{httpHeaderField.ServiceField.Name};");
                                }
                            }

                            code.WriteLine("return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest)");
                            using (code.Indent())
                                using (code.Block(".then(result => {", "});"))
                                {
                                    code.WriteLine("const status = result.response.status;");
                                    code.WriteLine("let value" + IfTypeScript($": I{capMethodName}Response | null") + " = null;");
                                    using (code.Block("if (result.json) {", "}"))
                                    {
                                        var validResponses = httpMethodInfo.ValidResponses;
                                        string elsePrefix  = "";
                                        foreach (var validResponse in validResponses)
                                        {
                                            string statusCodeAsString = ((int)validResponse.StatusCode).ToString(CultureInfo.InvariantCulture);
                                            code.WriteLine($"{elsePrefix}if (status === {statusCodeAsString}) {{");
                                            elsePrefix = "else ";

                                            using (code.Indent())
                                            {
                                                var bodyField = validResponse.BodyField;
                                                if (bodyField != null)
                                                {
                                                    string responseBodyFieldName = bodyField.ServiceField.Name;

                                                    var bodyFieldType = service.GetFieldType(bodyField.ServiceField);
                                                    if (bodyFieldType.Kind == ServiceTypeKind.Boolean)
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: true }};");
                                                    }
                                                    else
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: result.json }};");
                                                    }
                                                }
                                                else
                                                {
                                                    if (validResponse.NormalFields.Count == 0)
                                                    {
                                                        code.WriteLine("value = {};");
                                                    }
                                                    else
                                                    {
                                                        code.WriteLine("value = result.json;");
                                                    }
                                                }
                                            }
                                            code.WriteLine("}");
                                        }
                                    }

                                    using (code.Block("if (!value) {", "}"))
                                        code.WriteLine("return createResponseError(status, result.json)" + IfTypeScript($" as IServiceResult<I{capMethodName}Response>") + ";");

                                    if (httpMethodInfo.ResponseHeaderFields.Count != 0)
                                    {
                                        code.WriteLine("let headerValue" + IfTypeScript(": string | null | undefined") + ";");
                                        foreach (var httpHeaderField in httpMethodInfo.ResponseHeaderFields)
                                        {
                                            code.WriteLine($"headerValue = result.response.headers.get('{httpHeaderField.Name}');");
                                            using (code.Block("if (headerValue != null) {", "}"))
                                                code.WriteLine($"value.{httpHeaderField.Name} = headerValue;");
                                        }
                                    }

                                    code.WriteLine("return { value: value };");
                                }
                        }
                    }

                    if (TypeScript)
                    {
                        code.WriteLine();
                        code.WriteLine("private _fetch: IFetch;");
                        code.WriteLine("private _baseUri: string;");
                    }
                }
            }));

            if (Express)
            {
                namedTexts.Add(CreateFile(serverFileName, code =>
                {
                    code.WriteLine($"// DO NOT EDIT: generated by {GeneratorName}");

                    if (!TypeScript)
                    {
                        code.WriteLine("'use strict';");
                    }

                    code.WriteLine();
                    code.WriteLine("import * as bodyParser from 'body-parser';");
                    code.WriteLine("import * as express from 'express';");
                    code.WriteLine("import {" + IfTypeScript(" IServiceResult, IServiceError") + " } from 'facility-core';");
                    if (TypeScript)
                    {
                        code.WriteLine($"import {{ {string.Join(", ", typeNames)} }} from './{Uncapitalize(moduleName)}Types';");
                        code.WriteLine($"export * from './{Uncapitalize(moduleName)}Types';");
                    }

                    // TODO: export this from facility-core
                    code.WriteLine();
                    using (code.Block("const standardErrorCodes" + IfTypeScript(": { [code: string]: number }") + " = {", "};"))
                    {
                        code.WriteLine("'notModified': 304,");
                        code.WriteLine("'invalidRequest': 400,");
                        code.WriteLine("'notAuthenticated': 401,");
                        code.WriteLine("'notAuthorized': 403,");
                        code.WriteLine("'notFound': 404,");
                        code.WriteLine("'conflict': 409,");
                        code.WriteLine("'requestTooLarge': 413,");
                        code.WriteLine("'tooManyRequests': 429,");
                        code.WriteLine("'internalError': 500,");
                        code.WriteLine("'serviceUnavailable': 503,");
                    }

                    code.WriteLine();
                    using (code.Block("function parseBoolean(value" + IfTypeScript(": string | undefined") + ") {", "}"))
                    {
                        using (code.Block("if (typeof value === 'string') {", "}"))
                        {
                            code.WriteLine("const lowerValue = value.toLowerCase();");
                            using (code.Block("if (lowerValue === 'true') {", "}"))
                                code.WriteLine("return true;");
                            using (code.Block("if (lowerValue === 'false') {", "}"))
                                code.WriteLine("return false;");
                        }
                        code.WriteLine("return undefined;");
                    }

                    code.WriteLine();
                    using (code.Block("export function createApp(service" + IfTypeScript($": I{capModuleName}") + ", middleware" + IfTypeScript(": ((...args: any[]) => void)[]") + ")" + IfTypeScript(": express.Application") + " {", "}"))
                    {
                        code.WriteLine("const app = express();");
                        code.WriteLine("app.use(bodyParser.json());");
                        code.WriteLine("app.use(bodyParser.urlencoded({ extended: true }));");
                        code.WriteLine("middleware.forEach(func => app.use(func));");

                        foreach (var httpMethodInfo in httpServiceInfo.Methods)
                        {
                            string methodName    = httpMethodInfo.ServiceMethod.Name;
                            string capMethodName = CodeGenUtility.Capitalize(methodName);
                            string expressMethod = httpMethodInfo.Method.ToLowerInvariant();
                            string expressPath   = httpMethodInfo.Path;
                            foreach (var httpPathField in httpMethodInfo.PathFields)
                            {
                                expressPath = expressPath.Replace("{" + httpPathField.Name + "}", $":{httpPathField.Name}");
                            }

                            code.WriteLine();
                            WriteJsDoc(code, httpMethodInfo.ServiceMethod);
                            using (code.Block($"app.{expressMethod}('{expressPath}', function (req, res, next) {{", "});"))
                            {
                                code.WriteLine("const request" + IfTypeScript($": I{capMethodName}Request") + " = {};");

                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    code.WriteLine($"request.{httpPathField.ServiceField.Name} = {RenderJsConversion(httpPathField.ServiceField, service, $"req.params.{httpPathField.Name}")};");
                                }

                                foreach (var httpQueryField in httpMethodInfo.QueryFields)
                                {
                                    using (code.Block($"if (req.query['{httpQueryField.Name}'] != null) {{", "}"))
                                        code.WriteLine($"request.{httpQueryField.ServiceField.Name} = {RenderJsConversion(httpQueryField.ServiceField, service, $"req.query['{httpQueryField.Name}']")};");
                                }

                                if (httpMethodInfo.RequestBodyField != null)
                                {
                                    code.WriteLine($"request.{httpMethodInfo.RequestBodyField.ServiceField.Name} = req.body;");
                                }
                                else if (httpMethodInfo.RequestNormalFields != null)
                                {
                                    foreach (var field in httpMethodInfo.RequestNormalFields)
                                    {
                                        code.WriteLine($"request.{field.ServiceField.Name} = req.body.{field.ServiceField.Name};");
                                    }
                                }

                                if (httpMethodInfo.RequestHeaderFields != null)
                                {
                                    foreach (var field in httpMethodInfo.RequestHeaderFields)
                                    {
                                        code.WriteLine($"request.{field.ServiceField.Name} = req.header('{field.Name}');");
                                    }
                                }

                                code.WriteLine();
                                code.WriteLine($"return service.{methodName}(request)");

                                using (code.Indent())
                                {
                                    using (code.Block(".then(result => {", "})"))
                                    {
                                        using (code.Block("if (result.error) {", "}"))
                                        {
                                            code.WriteLine("const status = result.error.code && standardErrorCodes[result.error.code] || 500;");
                                            code.WriteLine("res.status(status).send(result.error);");
                                            code.WriteLine("return;");
                                        }
                                        using (code.Block("if (result.value) {", "}"))
                                        {
                                            if (httpMethodInfo.ResponseHeaderFields != null)
                                            {
                                                foreach (var field in httpMethodInfo.ResponseHeaderFields)
                                                {
                                                    using (code.Block($"if (result.value.{field.ServiceField.Name} != null) {{", "}"))
                                                        code.WriteLine($"res.setHeader('{field.Name}', result.value.{field.ServiceField.Name});");
                                                }
                                            }

                                            foreach (var validResponse in httpMethodInfo.ValidResponses.Where(x => x.NormalFields == null || x.NormalFields.Count == 0))
                                            {
                                                var bodyField = validResponse.BodyField;
                                                if (bodyField != null)
                                                {
                                                    string responseBodyFieldName = bodyField.ServiceField.Name;

                                                    using (code.Block($"if (result.value.{responseBodyFieldName}) {{", "}"))
                                                    {
                                                        var bodyFieldType = service.GetFieldType(bodyField.ServiceField);
                                                        if (bodyFieldType.Kind == ServiceTypeKind.Boolean)
                                                        {
                                                            code.WriteLine($"res.sendStatus({(int) validResponse.StatusCode});");
                                                            code.WriteLine("return;");
                                                        }
                                                        else
                                                        {
                                                            code.WriteLine($"res.status({(int) validResponse.StatusCode}).send(result.value.{responseBodyFieldName});");
                                                            code.WriteLine("return;");
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    if (validResponse.NormalFields.Count == 0)
                                                    {
                                                        code.WriteLine($"res.sendStatus({(int) validResponse.StatusCode});");
                                                        code.WriteLine("return;");
                                                    }
                                                }
                                            }

                                            foreach (var validResponse in httpMethodInfo.ValidResponses.Where(x => x.NormalFields != null && x.NormalFields.Count != 0))
                                            {
                                                code.WriteLine($"res.status({(int) validResponse.StatusCode}).send({{");
                                                using (code.Indent())
                                                {
                                                    foreach (var field in validResponse.NormalFields)
                                                    {
                                                        code.WriteLine($"{field.ServiceField.Name}: result.value.{field.ServiceField.Name},");
                                                    }
                                                }
                                                code.WriteLine("});");
                                                code.WriteLine("return;");
                                            }
                                        }

                                        code.WriteLine("throw new Error('Result must have an error or value.');");
                                    }
                                    code.WriteLine(".catch(next);");
                                }
                            }
                        }

                        code.WriteLine();
                        code.WriteLine("return app;");
                    }
                }));
            }

            return(new CodeGenOutput(namedTexts, new List <CodeGenPattern>()));
        }
Ejemplo n.º 19
0
 private static bool IsValidHeaderField(ServiceFieldInfo fieldInfo, ServiceInfo serviceInfo)
 {
     return(serviceInfo.GetFieldType(fieldInfo).Kind == ServiceTypeKind.String);
 }
Ejemplo n.º 20
0
        /// <summary>
        /// Generates the C# output.
        /// </summary>
        protected override CodeGenOutput GenerateOutputCore(ServiceInfo service)
        {
            var httpServiceInfo = new HttpServiceInfo(service);

            string moduleName    = ModuleName ?? service.Name;
            string capModuleName = CodeGenUtility.Capitalize(moduleName);

            return(new CodeGenOutput(CreateNamedText(Uncapitalize(moduleName) + (TypeScript ? ".ts" : ".js"), code =>
            {
                code.WriteLine($"// DO NOT EDIT: generated by {GeneratorName}");

                if (!TypeScript)
                {
                    code.WriteLine("'use strict';");
                }

                code.WriteLine();
                code.WriteLine("import { HttpClientUtility" + IfTypeScript(", IServiceResult, IServiceError, IHttpClientOptions") + " } from 'facility-core';");

                code.WriteLine();
                WriteJSDoc(code, $"Provides access to {capModuleName} over HTTP via fetch.");
                using (code.Block("export function createHttpClient({ fetch, baseUri }" + IfTypeScript(": IHttpClientOptions") + ")" + IfTypeScript($": I{capModuleName}") + " {", "}"))
                    code.WriteLine($"return new {capModuleName}HttpClient(fetch, baseUri);");

                if (TypeScript)
                {
                    code.WriteLine();
                    WriteJSDoc(code, service);
                    using (code.Block($"export interface I{capModuleName} {{", "}"))
                    {
                        foreach (var httpMethodInfo in httpServiceInfo.Methods)
                        {
                            string methodName = httpMethodInfo.ServiceMethod.Name;
                            string capMethodName = CodeGenUtility.Capitalize(methodName);
                            code.WriteLineSkipOnce();
                            WriteJSDoc(code, httpMethodInfo.ServiceMethod);
                            code.WriteLine($"{methodName}(request: I{capMethodName}Request): Promise<IServiceResult<I{capMethodName}Response>>;");
                        }
                    }

                    foreach (var methodInfo in service.Methods)
                    {
                        WriteDto(code, new ServiceDtoInfo(
                                     name: $"{CodeGenUtility.Capitalize(methodInfo.Name)}Request",
                                     fields: methodInfo.RequestFields,
                                     summary: $"Request for {CodeGenUtility.Capitalize(methodInfo.Name)}.",
                                     position: methodInfo.Position), service);

                        WriteDto(code, new ServiceDtoInfo(
                                     name: $"{CodeGenUtility.Capitalize(methodInfo.Name)}Response",
                                     fields: methodInfo.ResponseFields,
                                     summary: $"Response for {CodeGenUtility.Capitalize(methodInfo.Name)}.",
                                     position: methodInfo.Position), service);
                    }

                    foreach (var dtoInfo in service.Dtos)
                    {
                        WriteDto(code, dtoInfo, service);
                    }
                }

                code.WriteLine();
                code.WriteLine("const { fetchResponse, createResponseError, createRequiredRequestFieldError } = HttpClientUtility;");
                if (TypeScript)
                {
                    code.WriteLine("type IFetch = HttpClientUtility.IFetch;");
                    code.WriteLine("type IFetchRequest = HttpClientUtility.IFetchRequest;");
                }

                code.WriteLine();
                using (code.Block($"class {capModuleName}HttpClient" + IfTypeScript($" implements I{capModuleName}") + " {", "}"))
                {
                    using (code.Block("constructor(fetch" + IfTypeScript(": IFetch") + ", baseUri" + IfTypeScript(": string") + ") {", "}"))
                    {
                        using (code.Block("if (typeof fetch !== 'function') {", "}"))
                            code.WriteLine("throw new TypeError('fetch must be a function.');");
                        using (code.Block("if (typeof baseUri === 'undefined') {", "}"))
                            code.WriteLine($"baseUri = '{httpServiceInfo.Url ?? ""}';");
                        using (code.Block(@"if (/[^\/]$/.test(baseUri)) {", "}"))
                            code.WriteLine("baseUri += '/';");

                        code.WriteLine("this._fetch = fetch;");
                        code.WriteLine("this._baseUri = baseUri;");
                    }

                    foreach (var httpMethodInfo in httpServiceInfo.Methods)
                    {
                        string methodName = httpMethodInfo.ServiceMethod.Name;
                        string capMethodName = CodeGenUtility.Capitalize(methodName);

                        code.WriteLine();
                        WriteJSDoc(code, httpMethodInfo.ServiceMethod);
                        using (code.Block(IfTypeScript("public ") + $"{methodName}(request" + IfTypeScript($": I{capMethodName}Request") + ")" + IfTypeScript($": Promise<IServiceResult<I{capMethodName}Response>>") + " {", "}"))
                        {
                            bool hasPathFields = httpMethodInfo.PathFields.Count != 0;
                            string jsUriDelim = hasPathFields ? "`" : "'";
                            string jsUri = jsUriDelim + httpMethodInfo.Path.Substring(1) + jsUriDelim;
                            if (hasPathFields)
                            {
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    code.WriteLine($"const uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)} = request.{httpPathField.ServiceField.Name} != null && {RenderUriComponent(httpPathField.ServiceField, service)};");
                                    using (code.Block($"if (!uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}) {{", "}"))
                                        code.WriteLine($"return Promise.resolve(createRequiredRequestFieldError('{httpPathField.ServiceField.Name}'));");
                                }
                                foreach (var httpPathField in httpMethodInfo.PathFields)
                                {
                                    jsUri = jsUri.Replace("{" + httpPathField.Name + "}", $"${{uriPart{CodeGenUtility.Capitalize(httpPathField.ServiceField.Name)}}}");
                                }
                            }

                            bool hasQueryFields = httpMethodInfo.QueryFields.Count != 0;
                            code.WriteLine((hasQueryFields ? "let" : "const") + $" uri = {jsUri};");
                            if (hasQueryFields)
                            {
                                code.WriteLine("const query" + IfTypeScript(": string[]") + " = [];");
                                foreach (var httpQueryField in httpMethodInfo.QueryFields)
                                {
                                    code.WriteLine($"request.{httpQueryField.ServiceField.Name} == null || query.push('{httpQueryField.Name}=' + {RenderUriComponent(httpQueryField.ServiceField, service)});");
                                }
                                using (code.Block("if (query.length) {", "}"))
                                    code.WriteLine("uri = uri + '?' + query.join('&');");
                            }

                            using (code.Block("const fetchRequest" + IfTypeScript(": IFetchRequest") + " = {", "};"))
                            {
                                if (httpMethodInfo.RequestBodyField == null && httpMethodInfo.RequestNormalFields.Count == 0)
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}'");
                                }
                                else
                                {
                                    code.WriteLine($"method: '{httpMethodInfo.Method}',");
                                    code.WriteLine("headers: { 'Content-Type': 'application/json' },");

                                    if (httpMethodInfo.RequestBodyField != null)
                                    {
                                        code.WriteLine($"body: JSON.stringify(request.{httpMethodInfo.RequestBodyField.ServiceField.Name})");
                                    }
                                    else if (httpMethodInfo.ServiceMethod.RequestFields.Count == httpMethodInfo.RequestNormalFields.Count)
                                    {
                                        code.WriteLine("body: JSON.stringify(request)");
                                    }
                                    else
                                    {
                                        using (code.Block("body: JSON.stringify({", "})"))
                                        {
                                            for (int httpFieldIndex = 0; httpFieldIndex < httpMethodInfo.RequestNormalFields.Count; httpFieldIndex++)
                                            {
                                                var httpFieldInfo = httpMethodInfo.RequestNormalFields[httpFieldIndex];
                                                bool isLastField = httpFieldIndex == httpMethodInfo.RequestNormalFields.Count - 1;
                                                string fieldName = httpFieldInfo.ServiceField.Name;
                                                code.WriteLine(fieldName + ": request." + fieldName + (isLastField ? "" : ","));
                                            }
                                        }
                                    }
                                }
                            }

                            if (httpMethodInfo.RequestHeaderFields.Count != 0)
                            {
                                foreach (var httpHeaderField in httpMethodInfo.RequestHeaderFields)
                                {
                                    using (code.Block($"if (request.{httpHeaderField.ServiceField.Name} != null) {{", "}"))
                                        code.WriteLine($"fetchRequest.headers['{httpHeaderField.Name}'] = request.{httpHeaderField.ServiceField.Name};");
                                }
                            }

                            code.WriteLine("return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest)");
                            using (code.Indent())
                                using (code.Block(".then(result => {", "});"))
                                {
                                    code.WriteLine("const status = result.response.status;");
                                    code.WriteLine("let value" + IfTypeScript($": I{capMethodName}Response") + " = null;");
                                    using (code.Block($"if (result.json) {{", "}"))
                                    {
                                        var validResponses = httpMethodInfo.ValidResponses;
                                        string elsePrefix = "";
                                        foreach (var validResponse in validResponses)
                                        {
                                            string statusCodeAsString = ((int)validResponse.StatusCode).ToString(CultureInfo.InvariantCulture);
                                            code.WriteLine($"{elsePrefix}if (status === {statusCodeAsString}) {{");
                                            elsePrefix = "else ";

                                            using (code.Indent())
                                            {
                                                var bodyField = validResponse.BodyField;
                                                if (bodyField != null)
                                                {
                                                    string responseBodyFieldName = bodyField.ServiceField.Name;

                                                    var bodyFieldType = service.GetFieldType(bodyField.ServiceField);
                                                    if (bodyFieldType.Kind == ServiceTypeKind.Boolean)
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: true }};");
                                                    }
                                                    else
                                                    {
                                                        code.WriteLine($"value = {{ {responseBodyFieldName}: result.json }};");
                                                    }
                                                }
                                                else
                                                {
                                                    if (validResponse.NormalFields.Count == 0)
                                                    {
                                                        code.WriteLine("value = {};");
                                                    }
                                                    else
                                                    {
                                                        code.WriteLine("value = result.json;");
                                                    }
                                                }
                                            }
                                            code.WriteLine("}");
                                        }
                                    }

                                    using (code.Block("if (!value) {", "}"))
                                        code.WriteLine("return createResponseError(status, result.json)" + IfTypeScript($" as IServiceResult<I{capMethodName}Response>") + ";");

                                    if (httpMethodInfo.ResponseHeaderFields.Count != 0)
                                    {
                                        code.WriteLine("let headerValue" + IfTypeScript(": string") + ";");
                                        foreach (var httpHeaderField in httpMethodInfo.ResponseHeaderFields)
                                        {
                                            code.WriteLine($"headerValue = result.response.headers.get('{httpHeaderField.Name}');");
                                            using (code.Block("if (headerValue != null) {", "}"))
                                                code.WriteLine($"value.{httpHeaderField.Name} = headerValue;");
                                        }
                                    }

                                    code.WriteLine("return { value: value };");
                                }
                        }
                    }

                    if (TypeScript)
                    {
                        code.WriteLine();
                        code.WriteLine("private _fetch: IFetch;");
                        code.WriteLine("private _baseUri: string;");
                    }
                }
            })));
        }
Ejemplo n.º 21
0
 private static bool IsValidResponseBodyField(ServiceFieldInfo fieldInfo, ServiceInfo serviceInfo)
 {
     return(IsValidRequestBodyField(fieldInfo, serviceInfo) ||
            serviceInfo.GetFieldType(fieldInfo)?.Kind == ServiceTypeKind.Boolean);
 }