Ejemplo n.º 1
0
 public void Uncapitalize(string before, string after)
 {
     CodeGenUtility.Uncapitalize(before).Should().Be(after);
 }
Ejemplo n.º 2
0
        /// <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  = CodeGenUtility.Uncapitalize(moduleName) + "Types" + (TypeScript ? ".ts" : ".js");
            var clientFileName = CodeGenUtility.Uncapitalize(moduleName) + (TypeScript ? ".ts" : ".js");
            var serverFileName = CodeGenUtility.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, context?: unknown): 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);
                    }

                    foreach (var enumInfo in service.Enums)
                    {
                        typeNames.Add(enumInfo.Name);
                        code.WriteLine();
                        WriteJsDoc(code, enumInfo);
                        using (code.Block($"export enum {enumInfo.Name} {{", "}"))
                        {
                            foreach (var value in enumInfo.Values)
                            {
                                code.WriteLineSkipOnce();
                                WriteJsDoc(code, value);
                                code.WriteLine($"{value.Name} = '{value.Name}',");
                            }
                        }
                    }

                    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, $"./{CodeGenUtility.Uncapitalize(moduleName)}Types");
                    code.WriteLine($"export * from './{CodeGenUtility.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") + ", context" + IfTypeScript("?: unknown") + ")" + 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" + IfTypeScript("!") + $"['{httpHeaderField.Name}'] = request.{httpHeaderField.ServiceField.Name};");
                                }
                            }

                            code.WriteLine("return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context)");
                            using (code.Indent())
                                using (code.Block(".then(result => {", "});"))
                                {
                                    code.WriteLine("const status = result.response.status;");
                                    var responseValueType = $"I{capMethodName}Response";
                                    code.WriteLine("let value" + IfTypeScript($": {responseValueType} | 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 }}" + IfTypeScript($" as {responseValueType}") + ";");
                                                    }
                                                }
                                                else
                                                {
                                                    if (validResponse.NormalFields !.Count == 0)
                                                    {
                                                        code.WriteLine("value = {};");
                                                    }