redocly

How do I create separate API specifications for each endpoing in Redocly


I have an API with 73 endpoints. I can use redocly to split on the openapi.json to create seaprate Path and Component folders. I then want to take each Path document and create an api spec doc.

Bundle command won't do that, it expects the openapi.json doc and builds a new one from the split parts. The Join command combines multiple openapi.json docs into one. Still not what I want.

I really don't want to manually parse the Path files and build new docs. Is there a command I'm missing that will do this?


Solution

  • As Jeremy Fiel confirmed, there is no command to create individual docs from a split. So here is the code I wrote to accomplish this. The Split command creates a document "openapi.json". This document contains all the paths (endpoints) and the $ref to all the components. The key is to build an openapi spec document for each endpoint.

    My openapi.json doc started with the info block like this

     "openapi": "3.0.1",
      "info": {
        "title": "TC.Enterprise.eServicesOrchestrator.WebService",
        "description": "TC.Enterprise.eServicesOrchestrator.WebService v1.0.0",
        "version": "1.0.0"
    

    Then taking each path like this

    "/BestFitPharmacy/{programId}": {
          "$ref": "paths/BestFitPharmacy_{programId}.json"
    

    I combined them to create an openapi spec document.

    {
      "openapi":"3.0.1","info":{
        "title":"TC.Enterprise.eServicesOrchestrator.WebService",
        "description":"TC.Enterprise.eServicesOrchestrator.WebService v1.0.0","version":"1.0.0"
      }, 
      "paths":{
        "/BestFitPharmacy/{programId}":{
        "$ref": "paths/BestFitPharmacy_{programId}.json"
        }
      }
    }
    

    This document can then be processed by the redocly build-docs command and it will generate the openapi html document. The whole process I created, from start to finish, is as follows:

    1. Get the Swagger.json document generated at startup and save it.

    2. Run the redocly split command to generate the openapi.json doc, paths and components folders.

    3. Using the paths in openapi.json build individual openapi spec docs for each endpoint.

    4. Those documents can then be run through the redocly bundle-docs command to create the html documents.

    The first two are pretty straight forward. Here is the code I wrote to accomplish #3 &4. GenerateSwaggerDocs() creates the new openapi spec docs, which then calls the RunBundleCommand() to create the HTML doc. I have 78 endpoints and the whole thing takes about a minute.
    I'm not running the script from the same folder the docs are being generated. I'm doing that because I want the script to be in source control but not the resulting swagger docs.

    public static async Task GenerateSwaggerDocs ()
            {
                var sourceDir = Directory.GetCurrentDirectory();
                var destinationDir = "../bin/swagger/split/";
                var masterDoc = "../bin/swagger/split/openapi.json";
    
                var masterJson = JsonArray.Parse(File.ReadAllText(masterDoc));
                var apiText = masterJson["openapi"];
                var info = masterJson["info"];
                var paths = masterJson["paths"];
                var jsonHeader = string.Concat("openapi\":", apiText.ToJsonString(), ",", "\"info\":", info.ToJsonString(),",");
                JObject pathObject = new();
                
                if (paths != null)
                {
                    pathObject = JObject.Parse(paths.ToJsonString());
                }
    
                var result = pathObject.SelectTokens("data.symbols");
    
                if (!Directory.Exists(destinationDir))
                {
                    Directory.CreateDirectory(destinationDir);
                }
                
                foreach (var x in pathObject)
                {
                    var newJson = string.Concat($"{{\"{jsonHeader} \"paths\":{{", $"\"{x.Key}\":", $"{x.Value}}}}}");
                    //TODO: This is just to monitor the progress.  It can take a minute or two, to write all the files.  This can be removed once calling this is moved from Start.cs to a PS script.
                    Console.WriteLine(newJson);
                    var fileName = x.Key.Replace("{", "").Replace("}","").Replace("/","_").TrimStart('_'); 
                    await File.WriteAllTextAsync($"{destinationDir}{fileName}.json", newJson).ConfigureAwait(false);
                    
                    RunBundleCommand(fileName);
    
                }
            }
    
            private static void RunBundleCommand(string fileName)
            {
                string output = string.Empty;
                
                if(File.Exists($"../bin/swagger/split/{fileName}.json"))
                {
                    var args = $"bundle \"../bin/swagger/split/{fileName}.json\"  --output=\"../bin/swagger/finalDocs/{fileName}.json\"";
    
                    using (var process = new Process())
                    {
                        process.StartInfo = new ProcessStartInfo()
                        {
                            WorkingDirectory = Directory.GetCurrentDirectory(),
                            FileName = "redocly.cmd",
                            Arguments = args,
                            UseShellExecute = false,
                            RedirectStandardOutput = true,
                            RedirectStandardError = true
                        };
    
                        try
                        {
                            process.Start();
                            process.WaitForExit();
                            if (process.StandardOutput.ReadToEnd() != string.Empty)
                                output = process.StandardOutput.ReadToEnd();
    
                            else if (process.StandardError.ReadToEnd() is { } error)
                                output = error;
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex);
                        }
                    }
    
                    RunBuild_DocsCommand(fileName);
                }
            }