private void TransformModelTypes(CodeModelGo cmg)
        {
            foreach (var ctg in cmg.ModelTypes.Cast <CompositeTypeGo>())
            {
                var name = ctg.Name.FixedValue.TrimPackageName(cmg.Namespace);

                // ensure that the candidate name isn't already in use
                if (name != ctg.Name && cmg.ModelTypes.Any(mt => mt.Name == name))
                {
                    name = $"{name}Type";
                }

                if (CodeNamerGo.Instance.UserDefinedNames.Contains(name))
                {
                    name = $"{name}{cmg.Namespace.Capitalize()}";
                }

                ctg.SetName(name);
            }

            // Find all methods that returned paged results

            cmg.Methods.Cast <MethodGo>()
            .Where(m => m.IsPageable).ToList()
            .ForEach(m =>
            {
                if (!cmg.PagedTypes.ContainsKey(m.ReturnValue().Body))
                {
                    cmg.PagedTypes.Add(m.ReturnValue().Body, m.NextLink);
                }

                if (!m.NextMethodExists(cmg.Methods.Cast <MethodGo>()))
                {
                    cmg.NextMethodUndefined.Add(m.ReturnValue().Body);
                }
            });

            // Mark all models returned by one or more methods and note any "next link" fields used with paged data
            cmg.ModelTypes.Cast <CompositeTypeGo>()
            .Where(mtm =>
            {
                return(cmg.Methods.Cast <MethodGo>().Any(m => m.HasReturnValue() && m.ReturnValue().Body.Equals(mtm)));
            }).ToList()
            .ForEach(mtm =>
            {
                mtm.IsResponseType = true;
                if (cmg.PagedTypes.ContainsKey(mtm))
                {
                    mtm.NextLink       = CodeNamerGo.PascalCaseWithoutChar(cmg.PagedTypes[mtm], '.');
                    mtm.PreparerNeeded = cmg.NextMethodUndefined.Contains(mtm);
                }
            });
        }
        private void FixStutteringTypeNames(CodeModelGo cmg)
        {
            // Trim the package name from exported types; append a suitable qualifier, if needed, to avoid conflicts.
            var exportedTypes = new HashSet <object>();

            exportedTypes.UnionWith(cmg.EnumTypes);
            exportedTypes.UnionWith(cmg.Methods);
            exportedTypes.UnionWith(cmg.ModelTypes);

            var stutteringTypes = exportedTypes
                                  .Where(exported =>
                                         (exported is IModelType && (exported as IModelType).Name.FixedValue.StartsWith(cmg.Namespace, StringComparison.OrdinalIgnoreCase)) ||
                                         (exported is Method && (exported as Method).Name.FixedValue.StartsWith(cmg.Namespace, StringComparison.OrdinalIgnoreCase)));

            if (stutteringTypes.Any())
            {
                Logger.Instance.Log(Category.Warning, string.Format(CultureInfo.InvariantCulture, Resources.NamesStutter, stutteringTypes.Count()));
                stutteringTypes.ForEach(exported =>
                {
                    var name = exported is IModelType
                                        ? (exported as IModelType).Name
                                        : (exported as Method).Name;

                    Logger.Instance.Log(Category.Warning, string.Format(CultureInfo.InvariantCulture, Resources.StutteringName, name));

                    name = name.FixedValue.TrimPackageName(cmg.Namespace);

                    var nameInUse = exportedTypes
                                    .Any(et => (et is IModelType && (et as IModelType).Name.Equals(name)) || (et is Method && (et as Method).Name.Equals(name)));
                    if (exported is EnumType)
                    {
                        (exported as EnumType).Name.FixedValue = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Enum");
                    }
                    else if (exported is CompositeType)
                    {
                        (exported as CompositeType).Name.FixedValue = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Type");
                    }
                    else if (exported is Method)
                    {
                        (exported as Method).Name = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Method");
                    }
                });
            }
        }
        private static void FixStutteringTypeNames(CodeModelGo cmg)
        {
            // Trim the package name from exported types; append a suitable qualifier, if needed, to avoid conflicts.
            var exportedTypes = new HashSet <object>();

            exportedTypes.UnionWith(cmg.EnumTypes);
            exportedTypes.UnionWith(cmg.Methods);
            exportedTypes.UnionWith(cmg.ModelTypes);

            var stutteringTypes = exportedTypes
                                  .Where(exported =>
                                         (exported is IModelType modelType && modelType.Name.FixedValue.StartsWith(cmg.Namespace, StringComparison.OrdinalIgnoreCase)) ||
                                         (exported is Method method && method.Name.FixedValue.StartsWith(cmg.Namespace, StringComparison.OrdinalIgnoreCase)));

            if (stutteringTypes.Any())
            {
                stutteringTypes.ForEach(exported =>
                {
                    var name = exported is IModelType type
                                        ? type.Name
                                        : ((Method)exported).Name;

                    name = name.Value.TrimPackageName(cmg.Namespace);

                    var nameInUse = exportedTypes
                                    .Any(et => (et is IModelType modeltype && modeltype.Name.Equals(name)) || (et is Method methodType && methodType.Name.Equals(name)));
                    if (exported is EnumType enumType)
                    {
                        enumType.Name.Value = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Enum");
                    }
                    else if (exported is CompositeType compositeType)
                    {
                        compositeType.Name.Value = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Type");
                    }
                    else if (exported is Method methodType)
                    {
                        methodType.Name.Value = CodeNamerGo.AttachTypeName(name, cmg.Namespace, nameInUse, "Method");
                    }
                });
            }
        }
Exemple #4
0
        /// <summary>
        /// Generates Go code for service client.
        /// </summary>
        /// <param name="serviceClient"></param>
        /// <returns></returns>
        public override async Task Generate(CodeModel cm)
        {
            var folder = Path.GetFullPath(Settings.Instance.Host.GetValue <string>("output-folder").Result).Replace('\\', '/');
            // check if the namespace contains illegal characters
            var ns = Settings.Instance.Host.GetValue <string>("namespace").Result;

            if (Settings.Instance.Host.GetValue <bool>("namespace-chk").Result)
            {
                NamespaceCheck(ns);
            }

            // if preview-chk:true is specified verify that preview swagger is output under a preview subdirectory.
            // this is a bit of a hack until we have proper support for this in the swagger->sdk bot so it's opt-in.
            if (Settings.Instance.Host.GetValue <bool>("preview-chk").Result)
            {
                PreviewCheck(folder);
            }

            var codeModel = cm as CodeModelGo;

            if (codeModel == null)
            {
                throw new Exception("Code model is not a Go Code Model");
            }

            // unfortunately there is an ordering issue here.  during model generation we might
            // flatten some types (but not all depending on type).  then during client generation
            // the validation codegen needs to know if a type was flattened so it can generate
            // the correct code, so we need to generate models before clients.

            // Models
            var modelsTemplate = new ModelsTemplate
            {
                Model = codeModel
            };

            await Write(modelsTemplate, FormatFileName("models"));

            // Enums - while initially enums should be part of the models, to decrease the size of the models.go file,
            // we separate the enums definitions out of the models.go file
            var enums = codeModel.EnumTypes.Cast <EnumTypeGo>().ToList();

            if (enums.Any())
            {
                var enumsTemplate = new EnumsTemplate
                {
                    Model = codeModel
                };
                await Write(enumsTemplate, FormatFileName("enums"));
            }

            // Service client
            var serviceClientTemplate = new ServiceClientTemplate
            {
                Model = codeModel
            };

            await Write(serviceClientTemplate, FormatFileName("client"));

            // by convention the methods in the method group with an empty
            // name go into the client template so skip them here.
            HashSet <string> ReservedFiles = new HashSet <string>(StringComparer.OrdinalIgnoreCase)
            {
                "models",
                "client",
                "version",
                "interfaces",
                "enums",
            };

            foreach (var methodGroup in codeModel.MethodGroups.Where(mg => !string.IsNullOrEmpty(mg.Name)))
            {
                if (ReservedFiles.Contains(methodGroup.Name.Value))
                {
                    methodGroup.Name += "group";
                }
                ReservedFiles.Add(methodGroup.Name);
                var methodGroupTemplate = new MethodGroupTemplate
                {
                    Model = methodGroup
                };
                await Write(methodGroupTemplate, FormatFileName(methodGroup.Name).ToLowerInvariant());
            }

            // interfaces
            var interfacesTemplate = new InterfacesTemplate {
                Model = codeModel
            };

            await Write(interfacesTemplate, FormatFileName($"{CodeNamerGo.InterfacePackageName(codeModel.Namespace)}/interfaces"));

            // Version
            var versionTemplate = new VersionTemplate {
                Model = codeModel
            };

            await Write(versionTemplate, FormatFileName("version"));

            // go.mod file, opt-in by specifying the gomod-root arg
            var modRoot = Settings.Instance.Host.GetValue <string>("gomod-root").Result;

            if (!string.IsNullOrWhiteSpace(modRoot))
            {
                var i = folder.IndexOf(modRoot);
                if (i == -1)
                {
                    throw new Exception($"didn't find module root '{modRoot}' in output path '{folder}'");
                }
                var goVersion = Settings.Instance.Host.GetValue <string>("go-version").Result;
                if (string.IsNullOrWhiteSpace(goVersion))
                {
                    goVersion = defaultGoVersion;
                }
                // module name is everything to the right of the start of the module root
                var gomodTemplate = new GoModTemplate {
                    Model = new GoMod(folder.Substring(i), goVersion)
                };
                await Write(gomodTemplate, $"{StagingDir()}go.mod");
            }

            // metadata
            var metadataOutputFolder = Settings.Instance.Host.GetValue <string>("metadata-output-folder").Result;

            if (!string.IsNullOrWhiteSpace(metadataOutputFolder))
            {
                var metadataTemplate = new MetadataTemplate
                {
                    Model = new MetadataGo(Settings.Instance.Host.GetValue <string[]>("input-file").Result, folder, ns)
                };
                var tag = Settings.Instance.Host.GetValue <string>("tag").Result;
                await Write(metadataTemplate, $"{metadataOutputFolder}/{tag}.json");
            }
        }
        /// <summary>
        /// Generates Go code for service client.
        /// </summary>
        /// <param name="serviceClient"></param>
        /// <returns></returns>
        public override async Task Generate(CodeModel cm)
        {
            // if preview-chk:true is specified verify that preview swagger is output under a preview subdirectory.
            // this is a bit of a hack until we have proper support for this in the swagger->sdk bot so it's opt-in.
            if (Settings.Instance.Host.GetValue <bool>("preview-chk").Result)
            {
                const string previewSubdir = "/preview/";
                var          files         = await Settings.Instance.Host.GetValue <string[]>("input-file");

                // only evaluate composite builds if all swaggers are preview as we don't have a well-defined model for mixed preview/stable swaggers
                if (files.All(file => file.IndexOf(previewSubdir) > -1) &&
                    Settings.Instance.Host.GetValue <string>("output-folder").Result.IndexOf(previewSubdir) == -1)
                {
                    throw new InvalidOperationException($"codegen for preview swagger {files[0]} must be under a preview subdirectory");
                }
            }

            var codeModel = cm as CodeModelGo;

            if (codeModel == null)
            {
                throw new Exception("Code model is not a Go Code Model");
            }

            // unfortunately there is an ordering issue here.  during model generation we might
            // flatten some types (but not all depending on type).  then during client generation
            // the validation codegen needs to know if a type was flattened so it can generate
            // the correct code, so we need to generate models before clients.

            // Models
            var modelsTemplate = new ModelsTemplate
            {
                Model = codeModel
            };

            await Write(modelsTemplate, FormatFileName("models"));

            // Service client
            var serviceClientTemplate = new ServiceClientTemplate
            {
                Model = codeModel
            };

            await Write(serviceClientTemplate, FormatFileName("client"));

            // by convention the methods in the method group with an empty
            // name go into the client template so skip them here.
            HashSet <string> ReservedFiles = new HashSet <string>(StringComparer.OrdinalIgnoreCase)
            {
                "models",
                "client",
                "version",
                "interfaces",
            };

            foreach (var methodGroup in codeModel.MethodGroups.Where(mg => !string.IsNullOrEmpty(mg.Name)))
            {
                if (ReservedFiles.Contains(methodGroup.Name.Value))
                {
                    methodGroup.Name += "group";
                }
                ReservedFiles.Add(methodGroup.Name);
                var methodGroupTemplate = new MethodGroupTemplate
                {
                    Model = methodGroup
                };
                await Write(methodGroupTemplate, FormatFileName(methodGroup.Name).ToLowerInvariant());
            }

            // interfaces
            var interfacesTemplate = new InterfacesTemplate {
                Model = codeModel
            };

            await Write(interfacesTemplate, FormatFileName($"{CodeNamerGo.InterfacePackageName(codeModel.Namespace)}/interfaces"));

            // Version
            var versionTemplate = new VersionTemplate {
                Model = codeModel
            };

            await Write(versionTemplate, FormatFileName("version"));

            // go.mod file, opt-in by specifying the gomod-root arg
            var modRoot = Settings.Instance.Host.GetValue <string>("gomod-root").Result;

            if (!string.IsNullOrWhiteSpace(modRoot))
            {
                var normalized = Settings.Instance.Host.GetValue <string>("output-folder").Result.Replace('\\', '/');
                var i          = normalized.IndexOf(modRoot);
                if (i == -1)
                {
                    throw new Exception($"didn't find module root '{modRoot}' in output path '{normalized}'");
                }
                // module name is everything to the right of the start of the module root
                var gomodTemplate = new GoModTemplate {
                    Model = new GoMod(normalized.Substring(i))
                };
                await Write(gomodTemplate, $"{StagingDir()}go.mod");
            }
        }