Ejemplo n.º 1
0
        /// <summary>
        /// Método de extensão da interface: IServiceCollection
        /// Propriedades personalizadas
        /// </summary>
        /// <param name="services"></param>
        public static void ConfigSwaggerServices(this IServiceCollection services)
        {
            SwaggerContact.Config("Joseé", "My github");
            SwaggerInfo.Config("v1", "Josézinho", "Test");

            services.ConfigureServicesSwagger(true);
        }
Ejemplo n.º 2
0
        static void Main(string[] args)
        {
            // Parse options and show helptext if insufficient
            Options o = new Options();

            Parser.Default.ParseArguments(args, o);
            if (!o.IsValid())
            {
                var help = HelpText.AutoBuild(o);
                Console.WriteLine(help.ToString());
                return;
            }

            // First parse the file
            SwaggerRenderTask task = ParseRenderTask(o);

            if (task == null)
            {
                return;
            }

            // Download the swagger file
            SwaggerInfo api = DownloadSwaggerJson(o, task);

            if (api == null)
            {
                return;
            }

            // Render output
            Console.WriteLine($"***** Beginning render stage");
            Render(task, api);
        }
Ejemplo n.º 3
0
        private void FixupOneFile(SwaggerInfo api, RenderFixupTask fixup)
        {
            var fn = Path.Combine(rootFolder, fixup.file);

            Console.Write($"Executing fixup for {fn}... ");
            if (!File.Exists(fn))
            {
                Console.WriteLine(" File not found!");
            }
            else
            {
                // Determine what the new string is
                var newstring = QuickStringMerge(fixup.replacement, api);

                // What encoding did they want - basically everyone SHOULD want UTF8, but ascii is possible I guess
                Encoding e = Encoding.UTF8;
                if (fixup.encoding == "ASCII")
                {
                    e = Encoding.ASCII;
                }

                // Execute the fixup
                ReplaceStringInFile(fn, fixup.regex, newstring, e);
                Console.WriteLine(" Done!");
            }
        }
Ejemplo n.º 4
0
        public void ScanApiInfo(SwaggerConfig config)
        {
            _resolver = new XmlCommentResolver(config.XmlComments);
            //所有路由信息
            var routeOptions = this._httpScanner.GetRuntimeRouteOptions();
            // config 包含配置和XML的注释信息

            SwaggerInfo swagger = new SwaggerInfo
            {
                Host        = config.Host,
                Info        = config.ApiInfo ?? new SwaggerApiInfo(),
                BasePath    = config.BasePath,
                Paths       = new Dictionary <string, Dictionary <string, SwaggerMethod> >(),
                Definitions = new Dictionary <string, SwaggerDefinition>(),
                Tags        = new List <SwaggerTag>()
            };

            ProcessPaths(swagger.Paths, swagger.Tags, routeOptions, swagger.Definitions, config);

            var settings = new JsonSerializerSettings
            {
                DefaultValueHandling = DefaultValueHandling.Ignore
            };

            this._swaggerJson = JsonConvert.SerializeObject(swagger, Formatting.Indented, settings);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Render this particular type of client library
        /// </summary>
        /// <param name="api"></param>
        /// <param name="rootPath"></param>
        public virtual void Render(SwaggerInfo api)
        {
            if (templates != null)
            {
                // Iterate through each template
                foreach (var template in templates)
                {
                    Console.WriteLine($"     Rendering {name}.{template.file}...");

                    // What type of template are we looking at?
                    switch (template.type)
                    {
                    // A single template file for the entire API
                    case TemplateType.singleFile:
                        RenderSingleFile(api, template);
                        break;

                    // A separate file for each method category in the API
                    case TemplateType.methodCategories:
                        RenderMethodCategories(api, template);
                        break;

                    // One file per category
                    case TemplateType.methods:
                        RenderMethods(api, template);
                        break;

                    // One file per model
                    case TemplateType.models:
                        RenderModels(api, template);
                        break;

                    // One file per model
                    case TemplateType.uniqueModels:
                        RenderUniqueModels(api, template);
                        break;

                    // One file per enum
                    case TemplateType.enums:
                        RenderEnums(api, template);
                        break;

                    // One file per model that is used by a CRUD method that returns a list (for Apex use)
                    case TemplateType.listModels:
                        RenderListModels(api, template);
                        break;
                    }
                }
            }

            // Are there any fixups?
            if (fixups != null)
            {
                foreach (var fixup in fixups)
                {
                    FixupOneFile(api, fixup);
                }
            }
        }
Ejemplo n.º 6
0
        private void RenderSingleFile(SwaggerInfo api, RenderTemplateTask template)
        {
            var outputPath = Path.Combine(rootFolder, template.output);

            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
            var output = template.razor.ExecuteTemplate(api, null, null, null);

            File.WriteAllText(outputPath, output);
        }
 private void RenderEnums(SwaggerInfo api, RenderTemplateTask template)
 {
     foreach (var enumDataType in api.Enums)
     {
         var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, enumDataType));
         Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
         var output = template.razor.ExecuteTemplate(api, null, null, enumDataType);
         File.WriteAllText(outputPath, output);
     }
 }
 /// <summary>
 /// Execute this template against the specified model
 /// </summary>
 /// <param name="model"></param>
 /// <param name="m"></param>
 /// <param name="e"></param>
 /// <returns></returns>
 public virtual string ExecuteTemplate(SwaggerInfo api, MethodInfo method, ModelInfo model, EnumInfo enumDataType)
 {
     Buffer.Clear();
     SwaggerModel = api;
     MethodModel  = method;
     ClassModel   = model;
     EnumModel    = enumDataType;
     Execute();
     return(Buffer.ToString());
 }
Ejemplo n.º 9
0
 private void RenderMethods(SwaggerInfo api, RenderTemplateTask template)
 {
     foreach (var method in api.Methods)
     {
         var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, method));
         Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
         var output = template.razor.ExecuteTemplate(api, method, null, null);
         File.WriteAllText(outputPath, output);
     }
 }
Ejemplo n.º 10
0
        public static void Render(SwaggerRenderTask task, SwaggerInfo api)
        {
            // Render each target
            foreach (var target in task.targets)
            {
                Console.WriteLine($"***** Rendering {target.name}");
                target.Render(api);
            }

            // Done
            Console.WriteLine("***** Done");
        }
        public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
        {
            var         swagggersection = configuration.GetSection("swaggerInfo");
            SwaggerInfo swagggerOptions = swagggersection.Get <SwaggerInfo>();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version        = "v1",
                    Title          = swagggerOptions.TitleConf,
                    Description    = swagggerOptions.Description,
                    TermsOfService = new Uri("https://example.com/terms"),
                    Contact        = new OpenApiContact
                    {
                        Name  = "Globo",
                        Email = string.Empty,
                        Url   = new Uri("https://twitter.com/"),
                    },
                    License = new OpenApiLicense
                    {
                        Name = "Use under Globo",
                        Url  = new Uri("https://example.com/license"),
                    }
                });
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Name         = "Authorization",
                    Type         = SecuritySchemeType.ApiKey,
                    Scheme       = "Bearer",
                    BearerFormat = "JWT",
                    In           = ParameterLocation.Header,
                    Description  = "JWT Authorization header using the Bearer scheme."
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id   = "Bearer"
                            }
                        },
                        new string[] {}
                    }
                });
            });
        }
Ejemplo n.º 12
0
        private void RenderFetchModel(SwaggerInfo api, RenderTemplateTask template)
        {
            var oldModels = api.Models;

            api.Models = (from m in api.Models where m.SchemaName.StartsWith("FetchResult") select m).ToList();
            foreach (var model in api.Models)
            {
                var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, model));
                Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                var output = template.razor.ExecuteTemplate(api, null, model, null);
                File.WriteAllText(outputPath, output);
            }
            api.Models = oldModels;
        }
Ejemplo n.º 13
0
        public SwaggerInfo KeyWordFilter(SwaggerInfo api, string templateType)
        {
            // populate set
            if (KeyWordSet.Count == 0)
            {
                foreach (var w in KeyWordList)
                {
                    KeyWordSet.Add(w);
                }
            }

            // if we are filtering enum names
            if (templateType == "enum")
            {
                foreach (var enumType in api.Enums)
                {
                    foreach (var e in enumType.Values)
                    {
                        // add "Field" to each conflicted enum name to prevent compile error in Apex
                        if (KeyWordSet.Contains(e.Name))
                        {
                            e.Name = e.Name + "Field";
                        }
                    }
                }
            }

            // if we are filtering model property names
            if (templateType == "model")
            {
                foreach (var model in api.Models)
                {
                    foreach (var property in model.Properties)
                    {
                        // two types of property name, both needs to be filtered for key words
                        if (KeyWordSet.Contains(property.CleanParamName))
                        {
                            property._CleanParamName = property.CleanParamName + "Field";
                        }
                        else if (KeyWordSet.Contains(property.StrippedPackageParamName))
                        {
                            property._StrippedPackageParamName = property.StrippedPackageParamName + "Field";
                        }
                    }
                }
            }

            // return new api file
            return(api);
        }
Ejemplo n.º 14
0
        private void RenderEnums(SwaggerInfo api, RenderTemplateTask template)
        {
            foreach (var enumInfo in api.Enums)
            {
                // ErrorCodeId is not needed in Apex, all error codes are handled as String
                if ((template.file.Contains("apex_enum_class") || template.file.Contains("apex_meta")) && enumInfo.Name == "ErrorCodeId")
                {
                    continue;
                }

                var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, enumInfo));
                Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                var output = template.razor.ExecuteTemplate(api, null, null, enumInfo);
                File.WriteAllText(outputPath, output);
            }
        }
        public static void AddSwagger(this IApplicationBuilder app, IConfiguration configuration)
        {
            var         swagggersection = configuration.GetSection("swaggerInfo");
            SwaggerInfo swagggerOptions = swagggersection.Get <SwaggerInfo>();

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", swagggerOptions.TitleApp);
                c.RoutePrefix = swagggerOptions.Route;
            });
        }
Ejemplo n.º 16
0
        private void RenderMethodCategories(SwaggerInfo api, RenderTemplateTask template)
        {
            var categories = (from m in api.Methods select m.Category).Distinct();

            foreach (var c in categories)
            {
                var oldMethods = api.Methods;
                api.Methods = (from m in api.Methods where m.Category == c select m).ToList();
                var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, c));
                Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                template.razor.Category = c;
                var output = template.razor.ExecuteTemplate(api, null, null, null);
                template.razor.Category = null;
                File.WriteAllText(outputPath, output);
                api.Methods = oldMethods;
            }
        }
Ejemplo n.º 17
0
        public string PhpTypeComment(SwaggerInfo s, ParameterInfo p)
        {
            string comment = "";

            if (p.Comment != null)
            {
                comment = NoNewlines(FixWhitespace(p.Comment));
            }

            // Is this an enum?  If so, convert it to a string - we'll add a comment later
            if (IsEnumType(p.TypeName))
            {
                return(comment + " (See " + p.TypeName.Replace("?", "") + "::* for a list of allowable values)");
            }
            else if (p.TypeName == "Byte[]")
            {
                return(comment + " (This value is encoded as a Base64 string)");
            }

            return(comment);
        }
Ejemplo n.º 18
0
 private void RenderListModels(SwaggerInfo api, RenderTemplateTask template)
 {
     foreach (var method in api.Methods)
     {
         if (method.ResponseType == "array")
         {
             string modelName = method.parseBracket(method.ResponseTypeName).Substring(4);
             foreach (var model in api.Models)
             {
                 if (model.SchemaName.Contains(modelName) && !model.SchemaName.Contains("FetchResult"))
                 {
                     var outputPath = Path.Combine(rootFolder, QuickStringMerge(template.output, model));
                     Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                     var output = template.razor.ExecuteTemplate(api, null, model, null);
                     File.WriteAllText(outputPath, output);
                     break;
                 }
             }
         }
     }
 }
Ejemplo n.º 19
0
        private SwaggerInfo HandleFilter(SwaggerInfo api, RenderTemplateTask template)
        {
            string templateName = template.file;

            // handle all Apex related filtering
            if (templateName.Contains("apex"))
            {
                ApexFilters filter = new ApexFilters();

                // filtering for Apex enum & model classes
                if (templateName.Contains("apex_enum_class"))
                {
                    return(filter.KeyWordFilter(api, "enum"));
                }
                else if (templateName.Contains("apex_model_class"))
                {
                    return(filter.KeyWordFilter(api, "model"));
                }
            }

            // echo back the original api file if no changes is required
            return(api);
        }
Ejemplo n.º 20
0
        private static SwaggerInfo DownloadSwaggerJson(Options o, SwaggerRenderTask task)
        {
            string      swaggerJson = null;
            SwaggerInfo api         = null;

            // Download the swagger JSON file from the server
            try {
                Console.WriteLine($"***** Downloading swagger JSON from {task.swaggerUri}");
                var response = _client.GetAsync(task.swaggerUri).Result;
                swaggerJson = response.Content.ReadAsStringAsync().Result;
            } catch (Exception ex) {
                Console.WriteLine($"Exception downloading swagger JSON file: {ex.ToString()}");
                return(null);
            }

            // Parse the swagger JSON file
            try {
                Console.WriteLine($"***** Processing swagger JSON");
                api = ProcessSwagger(swaggerJson);
            } catch (Exception ex) {
                Console.WriteLine($"Exception processing swagger JSON file: {ex.ToString()}");
            }
            return(api);
        }
Ejemplo n.º 21
0
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddCors(options =>
            {
                options.AddPolicy("AllowOrigin",
                                  builder => builder.WithOrigins("http://localhost:3000"));
            });

            _swaggerInfo = Configuration.GetSection(nameof(SwaggerInfo)).Get <SwaggerInfo>();
            var tokenOptions = Configuration.GetSection(nameof(TokenOptions)).Get <TokenOptions>();

            services.AddSingleton(tokenOptions);

            #region Swagger Settings

            services.AddSwaggerGen(options =>
            {
                options.SwaggerGeneratorOptions.IgnoreObsoleteActions = true;

                options.AddSecurityDefinition(_swaggerInfo.Authorization.AuthenticationScheme, new OpenApiSecurityScheme
                {
                    Description = _swaggerInfo.Authorization.Description,
                    Name        = _swaggerInfo.Authorization.Name,
                    In          = ParameterLocation.Header,
                    Type        = SecuritySchemeType.ApiKey
                });

                var security = new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Id   = "Bearer",
                                Type = ReferenceType.SecurityScheme
                            },
                            UnresolvedReference = true
                        },
                        new List <string>()
                    }
                };
                options.AddSecurityRequirement(security);
                options.SwaggerDoc(_swaggerInfo.Version, new OpenApiInfo
                {
                    Title       = _swaggerInfo.Title,
                    Version     = _swaggerInfo.Version,
                    Description = _swaggerInfo.Description,
                    Contact     = new OpenApiContact
                    {
                        Name  = _swaggerInfo.Contact.Name,
                        Email = _swaggerInfo.Contact.Email
                    }
                });
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                options.IncludeXmlComments(xmlPath);
            });

            #endregion

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer           = true,
                    ValidateAudience         = true,
                    ValidateLifetime         = true,
                    ValidIssuer              = tokenOptions.Issuer,
                    ValidAudience            = tokenOptions.Audience,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey         = tokenOptions.Key.CreateSecurityKey()
                };
            });

            services.AddDependencyResolvers(new ICoreModule[]
            {
                new CoreModule()
            });
        }
Ejemplo n.º 22
0
        private static SwaggerInfo Cleanup(SwaggerModel obj)
        {
            SwaggerInfo result = new SwaggerInfo();

            result.ApiVersion = obj.ApiVersion;

            // Set up alternative version numbers: This one does not permit dashes
            result.ApiVersionPeriodsOnly = result.ApiVersion.Replace("-", ".");

            // Set up alternative version numbers: This one permits only three segments
            var sb         = new StringBuilder();
            int numPeriods = 0;

            foreach (char c in obj.ApiVersion)
            {
                if (c == '.')
                {
                    numPeriods++;
                }
                if (numPeriods > 3 || c == '-')
                {
                    break;
                }
                sb.Append(c);
            }
            result.ApiVersionThreeSegmentsOnly = sb.ToString();

            // Loop through all paths and spit them out to the console
            foreach (var path in (from p in obj.paths orderby p.Key select p))
            {
                foreach (var verb in path.Value)
                {
                    // Set up our API
                    MethodInfo api = new MethodInfo();
                    api.URI         = path.Key;
                    api.HttpVerb    = verb.Key;
                    api.Summary     = verb.Value.summary;
                    api.Description = verb.Value.description;
                    api.Params      = new List <ParameterInfo>();
                    api.Category    = verb.Value.tags.FirstOrDefault();
                    api.Name        = verb.Value.operationId;

                    // Now figure out all the URL parameters
                    foreach (var parameter in verb.Value.parameters)
                    {
                        // Construct parameter
                        var pi = ResolveType(parameter);

                        // Query String Parameters
                        if (parameter.paramIn == "query")
                        {
                            pi.ParameterLocation = ParameterLocationType.QueryString;

                            // URL Path parameters
                        }
                        else if (parameter.paramIn == "path")
                        {
                            pi.ParameterLocation = ParameterLocationType.UriPath;

                            // Body parameters
                        }
                        else if (parameter.paramIn == "body")
                        {
                            pi.ParamName         = "model";
                            pi.ParameterLocation = ParameterLocationType.RequestBody;
                            api.BodyParam        = pi;
                        }
                        else if (parameter.paramIn == "header")
                        {
                            pi.ParameterLocation = ParameterLocationType.Header;
                        }
                        else if (parameter.paramIn == "formData")
                        {
                            pi.ParameterLocation = ParameterLocationType.FormData;
                        }
                        else
                        {
                            throw new Exception("Unrecognized parameter location: " + parameter.paramIn);
                        }
                        api.Params.Add(pi);

                        // Is this property an enum?
                        if (parameter.EnumDataType != null)
                        {
                            ExtractEnum(result.Enums, parameter);
                        }
                    }

                    // Now figure out the response type
                    SwaggerResult ok = null;
                    if (verb.Value.responses.TryGetValue("200", out ok))
                    {
                        api.ResponseType     = ok.schema == null ? null : ok.schema.type;
                        api.ResponseTypeName = ResolveTypeName(ok.schema);
                    }
                    else if (verb.Value.responses.TryGetValue("201", out ok))
                    {
                        api.ResponseType     = ok.schema == null ? null : ok.schema.type;
                        api.ResponseTypeName = ResolveTypeName(ok.schema);
                    }

                    // Ensure that body parameters are always last for consistency
                    if (api.BodyParam != null)
                    {
                        api.Params.Remove(api.BodyParam);
                        api.Params.Add(api.BodyParam);
                    }

                    // Done with this API
                    result.Methods.Add(api);
                }
            }

            // Loop through all the schemas
            foreach (var def in obj.definitions)
            {
                var m = new ModelInfo()
                {
                    SchemaName  = def.Key,
                    Comment     = def.Value.description,
                    Example     = def.Value.example,
                    Description = def.Value.description,
                    Required    = def.Value.required,
                    Type        = def.Value.type,
                    Properties  = new List <ParameterInfo>()
                };
                foreach (var prop in def.Value.properties)
                {
                    if (!prop.Value.required && def.Value.required != null)
                    {
                        prop.Value.required = def.Value.required.Contains(prop.Key);
                    }

                    // Construct property
                    var pi = ResolveType(prop.Value);
                    pi.ParamName = prop.Key;
                    m.Properties.Add(pi);

                    // Is this property an enum?
                    if (prop.Value.EnumDataType != null)
                    {
                        ExtractEnum(result.Enums, prop.Value);
                    }
                }

                result.Models.Add(m);
            }

            //// Now add the enums we know we need.
            //// Because of the complex way this Dictionary<> is rendered in Swagger, it's hard to pick up the correct values.
            //var tat = (from e in result.Enums where e.EnumDataType == "TransactionAddressType" select e).FirstOrDefault();
            //if (tat == null) {
            //    tat = new EnumInfo()
            //    {
            //        EnumDataType = "TransactionAddressType",
            //        Items = new List<EnumItem>()
            //    };
            //    result.Enums.Add(tat);
            //}
            //tat.AddItem("ShipFrom", "This is the location from which the product was shipped");
            //tat.AddItem("ShipTo", "This is the location to which the product was shipped");
            //tat.AddItem("PointOfOrderAcceptance", "Location where the order was accepted; typically the call center, business office where purchase orders are accepted, server locations where orders are processed and accepted");
            //tat.AddItem("PointOfOrderOrigin", "Location from which the order was placed; typically the customer's home or business location");
            //tat.AddItem("SingleLocation", "Only used if all addresses for this transaction were identical; e.g. if this was a point-of-sale physical transaction");

            // Here's your processed API
            return(result);
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Render this particular type of client library
        /// </summary>
        /// <param name="api"></param>
        /// <param name="rootPath"></param>
        public virtual void Render(SwaggerInfo api)
        {
            if (templates != null)
            {
                // Iterate through each template
                foreach (var template in templates)
                {
                    Console.WriteLine($"     Rendering {name}.{template.file}...");

                    // What type of template are we looking at?
                    switch (template.type)
                    {
                    // A single template file for the entire API
                    case TemplateType.singleFile:
                        RenderSingleFile(api, template);
                        break;

                    // A separate file for each method category in the API
                    case TemplateType.methodCategories:
                        RenderMethodCategories(api, template);
                        break;

                    // One file per category
                    case TemplateType.methods:
                        RenderMethods(api, template);
                        break;

                    // One file per model
                    case TemplateType.models:
                        RenderModels(api, template);
                        break;

                    // One file per model
                    case TemplateType.uniqueModels:
                        SwaggerInfo tempApiForModel = HandleFilter(api, template);
                        RenderUniqueModels(tempApiForModel, template);
                        break;

                    // One file per enum
                    case TemplateType.enums:
                        SwaggerInfo tempApiForEnum = HandleFilter(api, template);
                        RenderEnums(tempApiForEnum, template);
                        break;

                    // One file per model that is used by a CRUD method that returns a list (for Apex use)
                    case TemplateType.listModels:
                        RenderListModels(api, template);
                        break;

                    // One file per model that is used by a CRUD method to fetch a collection of data. i.e FetchResult<SubscriptionModel> ListMySubscriptions() needs be an unique model (for Apex use)
                    case TemplateType.fetchModels:
                        RenderFetchModel(api, template);
                        break;
                    }
                }
            }

            // Are there any fixups?
            if (fixups != null)
            {
                foreach (var fixup in fixups)
                {
                    FixupOneFile(api, fixup);
                }
            }
        }