Swagger

How to generate Swagger documentation with Pip.Services.

Key takeaways

Swagger module Contains components used to generate Swagger UIs.
Swagger YAML file File used to declare methods to be documented via Swagger.
Swagger An Interface Description Language for describing RESTful APIs using JSON.

Introduction

In this tutorial, you will learn how to generate Swagger documentation for a REST controller. We will see three different cases. The first is a common REST controller, which is documented via a YAML file containing a description of its methods. The second is a commandable REST controller, which has a defined set of commands that is used to define the Swagger document. Finally, the last case considers a commandable REST component with a command set and a Swagger UI defined by a YAML file.

Swagger document generation

Pip.Services offers two types of REST controllers, which are defined by two different classes. The first is an ordinary REST controller and is defined by the RestController component. The second is a REST controller that contains a set of predefined commands (or methods) that can be called from other controllers and is defined by the CommandableHttpController class.

As such, they represent two different approaches when it comes to Swagger documentation: A REST controller needs a YAML file that describes its UI in order to generate its documentation, whereas a commandable controller allows for automatic generation via a description of the command set or via a YAML file if the path to it is included in the configuration file. Moreover, it should be noted that an automatically-generated description always considers an HTTP method as POST.

To explain these cases, we will create an app that given a name returns the phrase “Hello {name}” by calling a method named greeting. In this app, we will include the necessary elements to create a Swagger UI that documents this method. The following sections teach the steps to achieve this goal.

Pre-requisites

First of all, to create a Swagger UI, we need to install the swagger module. This can be done with the following command:

npm install pip-services4-swagger-node --save
Not available
Not available
Not available
pip install pip-services3-swagger
Not available

Document 1: REST controller

In this case, we want to document the greeting method as part of a REST controller. For this, we need to define a YAML file containing the information necessary to create the Swagger UI.

Controller

Our REST controller is called HelloFriendController. It is defined by a class that inherits from the RestController component and has a method named greetings, which given a name, returns “Hello {name}” on a web page.

It also contains a reference to the service and a method named register that defines the necessary elements for the Swagger UI. Its code is as follows:

import { ConfigParams, Descriptor, IReferences } from "pip-services4-components-node";
import { RestController } from "pip-services4-http-node";

class HelloFriendRestController extends RestController {
    private _service: HelloFriendService;

    // swagger
    private _swaggerContent: string;
    private _swaggerPath: string;

    public constructor() {
        super();
        this._baseRoute = "/hello_friend";

        let serviceDescriptor = new Descriptor("hello-friend", "service", "*", "*", "1.0");
        this._dependencyResolver.put("service", serviceDescriptor);
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // swagger
        this._swaggerContent = config.getAsNullableString("swagger.content");
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public setReferences(references: IReferences) {
        super.setReferences(references);
        this._service = this._dependencyResolver.getOneRequired<HelloFriendService>("service");
    }

    public register(): void {
        this.registerRoute("GET", "/greeting", null, this.greeting);

        // swagger
        if (this._swaggerContent != null)
            this.registerOpenApiSpec(this._swaggerContent);

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }

    public async greeting(req: any, res: any): Promise<void> {
        let name = req.query.name;
        let result = this._service.greeting(name);

        this.sendResult(req, res, result);
    }
}
Not available
Not available
Not available
from pip_services4_components.refer import Descriptor
from pip_services4_data.validate import Schema
from pip_services4_http.controller import RestController

import bottle


class HelloFriendRestController(RestController):

    def __init__(self):
        super(HelloFriendRestController, self).__init__()

        self._base_route = "/hello_friend"

        ServiceDescriptor = Descriptor('hello-friend', 'service', '*', '*', '1.0')
        self._dependency_resolver.put('service', ServiceDescriptor)
        self._service = None

        # Swagger
        self._swagger_content = None
        self._swagger_path = None

    def configure(self, config):
        super().configure(config)
        
        # Swagger
        self._swagger_content = config.get_as_nullable_string("swagger.content")
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def set_references(self, references):
        super(HelloFriendRestController, self).set_references(references)
        self._service = self._dependency_resolver.get_one_required('service')

    def register(self):
        self.register_route(method="GET", route="/greeting", schema=Schema(), handler=self.greeting)
        
        # Swagger
        if self._swagger_content:
            self._register_open_api_spec(self._swagger_content)

        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)

    def greeting(self):
        name = bottle.request.query.get('name')
        result = self._controller.greeting(name)
        return self.send_result(result)
Not available
Configuration

As we will use a process container to run the example, we need to describe this controller in the configuration file. In this description, we set the Swagger’s enable field to true to specify that we want to generate a Swagger UI for the controller, and we define the path to our YAML file containing the Swagger UI description.

# HTTP Controller V1
- descriptor: "hello-friend:controller:http:default:1.0"
  swagger:
    enable: true
    path: './rest_swagger.yml'

Not available
Not available
Not available
# HTTP Service V1
- descriptor: "hello-friend:controller:http:default:1.0"
  swagger:
    enable: true
    path: './rest_swagger.yml'
Not available
Swagger YAML file

Now, we create a YAML file that will be used by Swagger to define the UI. In our case, the controller has the greeting method only, which we consider of type GET. An example of this file is:

openapi: '3.0.2'
info:
  title: 'Friends Controller'
  description: 'REST API from YAML file'
  version: '1'
paths:
  /hello_friend/greeting:
    get:
      tags:
        - hello_friend
      parameters:
        - in: query
          name: name
          schema:
            type: string
          required: true
      responses:
        201:
          description: 'Successful response'
          content:
            application/json:
              schema:
                type: 'object'

Documents 2 & 3: Commandable REST controller

These two cases document the same commandable REST controller. The difference between them is that the first automatically generates the Swagger UI based on a command set, and the second uses a YAML file.

Command set

To create a command set, we extend the CommandSet class and define our greeting command in it. The code below illustrates how to do this:

import { TypeCode } from "pip-services4-commons-node";
import { Parameters, IContext } from "pip-services4-components-node";
import { ObjectSchema } from "pip-services4-data-node";
import { CommandSet, ICommand, Command } from "pip-services4-rpc-node";

class FriendsCommandSet extends CommandSet {
    private _service: HelloFriendService;

    public constructor(service: HelloFriendService) {
        super();

        this._service = service;

        this.addCommand(this.makeGreeting());
    }

    private makeGreeting(): ICommand {
        return new Command('greeeting', 
            new ObjectSchema(true).withRequiredProperty('name', TypeCode.String), 
            async (ctx: IContext, args: Parameters) =>
            {
                let name = args.getAsString("name");
                let res = this._service.greeting(name);
                return res;
            }
        );
    }
}
Not available
Not available
Not available
from pip_services4_rpc.commands import Command, CommandSet, ICommand
from pip_services4_components.exec import Parameters
from pip_services4_data.validate import Schema, ObjectSchema
from pip_services4_commons.convert import TypeCode
from typing import Optional

class FriendsCommandSet(CommandSet):
    _service: 'HelloFriendService'

    def __init__(self, service):
        super().__init__()

        self._service = controller

        self.add_command(self._make_greeting())

    def _make_greeting(self) -> ICommand:
        def handler(context: Optional[IContext], args: Parameters):
            name = args.get_as_string("name")
            res = self._controller.greeting(name)
            return res

        return Command(
            "greeting",
            ObjectSchema(True).with_required_property("name", TypeCode.String),
            handler
        )

Not available
Controller for document 2

Once our command set has been defined, we create our commandable REST controller by extending the CommandableHttpController class and we link it to our controller. This controller checks for a YAML file in the configuration file. If not found, it builds the Swagger UI from the command set. In our example, the configuration file doesn’t include a path to a YAML file, and the Swagger UI is generated from the command set previously defined.

import { Descriptor, ConfigParams } from "pip-services4-components-node";
import { CommandableHttpController } from "pip-services4-http-node";

class FriendCommandableHttpService1 extends CommandableHttpController {
    private _swaggerPath: string;

    public constructor() {
        super("commandable_hello_friend1");
        this._dependencyResolver.put('service', new Descriptor("hello-friend", "service", "*", "*", "*"));
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // Swagger
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public register(): void {
        super.register();

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }
}
Not available
Not available
Not available
from pip_services4_http.controller import CommandableHttpController

class FriendCommandableHttpController1(CommandableHttpController):

    def __init__(self):
        super().__init__('commandable_hello_friend1')
        self._dependency_resolver.put('service', Descriptor('hello-friend', 'service', '*', '*', '*'))

        self._swagger_path = None
        
    def configure(self, config):
        super().configure(config)

        # Swagger
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def register(self):
        super().register()

        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)

Not available
Configuration for document 2

To be able to generate a Swagger UI, we need to set the swagger’s enable field to true. Besides, as we want to document the commands defined in the command set, we declare auto as true and we define the route field that will be part of the URL for the generated Swagger UI. The example below shows this configuration.

- descriptor: "hello-friend:controller:commandable-http1:default:1.0"
  swagger:
    enable: true
    auto: true
    route: swagger
    name: Friends Controller
    description: Commandable REST API - Automatic
Controller for document 3

Similar to the previous one, this controller builds the Swagger UI from the YAML file defined in the configuration file.

import { Descriptor, ConfigParams } from "pip-services4-components-node";
import { CommandableHttpController } from "pip-services4-http-node";

class FriendCommandableHttpService2 extends CommandableHttpController {
    private _swaggerPath: string;

    public constructor() {
        super("commandable_hello_friend1");
        this._dependencyResolver.put('service', new Descriptor("hello-friend", "service", "*", "*", "*"));
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // Swagger
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public register(): void {
        super.register();

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }
}
Not available
Not available
Not available
from pip_services4_http.controller import CommandableHttpController

class FriendCommandableHttpController2(CommandableHttpController):

    def __init__(self):
        super().__init__('commandable_hello_friend2')
        self._dependency_resolver.put('service', Descriptor('hello-friend', 'service', '*', '*', '*'))

        self._swagger_path = None
        
    def configure(self, config):
        super().configure(config)

        # Swagger
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def register(self):
        super().register()

        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)
Not available
Configuration for document 3

In this case, we declare a path to a YAML file containing the description for the Swagger UI. As a result, even though we have declared auto as true, the system will choose this file over the automatic generation.

- descriptor: "hello-friend:controller:commandable-http2:default:1.0"
  swagger:
    enable: true
    auto: false
    route: swagger
    path: './commandable_swagger.yml'
Not available
Not available
Not available
- descriptor: "hello-friend:service:commandable-http2:default:1.0"
  swagger:
    enable: true
    auto: false
    route: swagger
    path: './commandable_swagger.yml'
Not available
Swagger yam file for document 3

Here, we use the YAML file below to describe the UI. As we can see, the main difference with the previous one is that we declare the HTTP method as POST instead of GET, and therefore, we define the requestBody as required.

openapi: '3.0.2'
info:
  title: 'Friends Controller'
  description: 'Commandable REST API from YAM file'
  version: '1'
paths:
  /commandable_hello_friend/greeting:
    post:
      tags:
        - commandable_hello_friend
      requestBody:
        required: true
        description: Friend name
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string

      responses:
        201:
          description: 'Successful response'
          content:
            application/json:
              schema:
                type: 'object'

Containerization

Now that our REST controllers are defined, we want to create a process container to run them. For this, we need to define our factory of components and a class extending ProcessContainer. The following sections explain how to do this.

Factory

To create our factory of components, we extend the Factory class and register our REST and commandable REST controllers.

import { Descriptor, Factory } from "pip-services4-components-node";

class HelloFriendServiceFactory extends Factory {
    public constructor()
    {
        super();
        
        let HttpControllerDescriptor = new Descriptor("hello-friend", "controller", "http", "*", "1.0");                          // Controller 1
        let CommandableHttpControllerDescriptor1 = new Descriptor("hello-friend", "controller", "commandable-http1", "*", "1.0"); // Controller 2
        let CommandableHttpontrollerDescriptor2 = new Descriptor("hello-friend", "controller", "commandable-http2", "*", "1.0"); // Controller 2
        let ServiceDescriptor = new Descriptor("hello-friend", "service", "default", "*", "1.0");                     // Service

        this.registerAsType(HttpControllerDescriptor, HelloFriendRestController);                      // View 1
        this.registerAsType(CommandableHttpControllerDescriptor1, FriendCommandableHttpController1);   // View 2
        this.registerAsType(CommandableHttpontrollerDescriptor2, FriendCommandableHttpController2);   // View 3
        this.registerAsType(ServiceDescriptor, HelloFriendService);                        // Service
    }
}
Not available
Not available
Not available
from pip_services4_components.refer import Descriptor
from pip_services4_components.build import Factory


class HelloFriendControllerFactory(Factory):
    def __init__(self):
        super(HelloFriendControllerFactory, self).__init__()

        HttpControllerDescriptor = Descriptor('hello-friend', 'controller', 'http', '*', '1.0')                          # View 1
        CommandableHttpControllerDescriptor1 = Descriptor('hello-friend', 'controller', 'commandable-http1', '*', '1.0') # View 2
        CommandableHttpControllerDescriptor2 = Descriptor('hello-friend', 'controller', 'commandable-http2', '*', '1.0') # View 2
        ServiceDescriptor = Descriptor('hello-friend', 'service', 'default', '*', '1.0')                     # Controller
                                                                                
            
        self.register_as_type(HttpControllerDescriptor, HelloFriendRestController)                       # Controller
        self.register_as_type(CommandableHttpControllerDescriptor1, FriendCommandableHttpController1)    # Controller
        self.register_as_type(CommandableHttpControllerDescriptor2, FriendCommandableHttpController2)    # Controller
        self.register_as_type(ServiceDescriptor, HelloFriendService)                         # Service
Not available
Process container

Once we have our factory, we define our process container by extending the ProcessContainer class and adding the factories for the controllers and Swagger. Our code will look something like this:

import { ProcessContainer } from "pip-services4-container-node";
import { DefaultSwaggerFactory } from "pip-services4-swagger-node";
import { DefaultHttpFactory } from "pip-services4-http-node";

class HelloFriendProcess extends ProcessContainer {
    public constructor() {
        super("hello-friend", "HelloFriend microservice");

        this._configPath = "./config.yml";
        this._factories.add(new HelloFriendServiceFactory());
        this._factories.add(new DefaultHttpFactory());
        this._factories.add(new DefaultSwaggerFactory());
    }
}

Not available
Not available
Not available
from pip_services4_container.container import ProcessContainer
from pip_services4_http.build import DefaultRpcFactory
from pip_services4_swagger.build.DefaultSwaggerFactory import DefaultSwaggerFactory



class HelloFriendProcess(ProcessContainer):

    def __init__(self):
        super(HelloFriendProcess, self).__init__('hello-friend', 'HelloFriend microservice')
        self._config_path = './config35.yaml'
        self._factories.add(HelloFriendControllerFactory())
        self._factories.add(DefaultRpcFactory())
        self._factories.add(DefaultSwaggerFactory())
Not available

Runner

After our components are defined, we can run our app by invoking the run method from our process container.

export async function main() {
    try {
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}

Not available
Not available
Not available
if __name__ == '__main__':
    runner = HelloFriendProcess()
    print("run")
    try:
        runner.run()
    except Exception as ex:
        print(ex)
Not available

And, after executing our code, we will see the following messages on our console:

figure 1

Results

To see the generated Swagger UIs, we can use the following URL:

http://localhost:8080/swagger/index.html

General Interface

The generated Swagger UI presents a drop-down menu that can be used to select any of the cases defined in this exercise.

figure 2

Document 1: REST controller

If we select the hello_friend option, we will see a UI that presents all the information defined in the Swagger YAML file.

figure 3

Document 2: Commandable REST controller

Alternatively, if we choose the commandable_hello_friend1 option, we will be presented by a UI showing the information automatically generated from the command set.

figure 4

Document 3: Commandable REST controller.

Finally, if we select commandable_hello_friend2, we get a similar UI but generated from our YAML file.

figure 5

Final code

In this section, we show the complete code and the corresponding configuration YAML file.

swagger.py
import { ProcessContainer } from "pip-services4-container-node";
import { DefaultSwaggerFactory } from "pip-services4-swagger-node";
import { CommandableHttpController, DefaultHttpFactory, RestController } from "pip-services4-http-node";
import { 
    ConfigParams, Descriptor, Factory, IConfigurable, 
    IReferences, IContext, Parameters 
} from "pip-services4-components-node";
import { CommandSet, ICommand, Command } from "pip-services4-rpc-node";
import { ObjectSchema } from "pip-services4-data-node";
import { TypeCode } from "pip-services4-commons-node";


export async function main() {
    // Runner
    try {
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}

// REST controller (Swagger UI from YAML file)

class HelloFriendRestController extends RestController {
    private _service: HelloFriendService;

    // swagger
    private _swaggerContent: string;
    private _swaggerPath: string;

    public constructor() {
        super();
        this._baseRoute = "/hello_friend";

        let serviceDescriptor = new Descriptor("hello-friend", "service", "*", "*", "1.0");
        this._dependencyResolver.put("service", serviceDescriptor);
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // swagger
        this._swaggerContent = config.getAsNullableString("swagger.content");
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public setReferences(references: IReferences) {
        super.setReferences(references);
        this._service = this._dependencyResolver.getOneRequired<HelloFriendService>("service");
    }

    public register(): void {
        this.registerRoute("GET", "/greeting", null, this.greeting);

        // swagger
        if (this._swaggerContent != null)
            this.registerOpenApiSpec(this._swaggerContent);

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }

    public async greeting(req: any, res: any): Promise<void> {
        let name = req.query.name;
        let result = this._service.greeting(name);

        this.sendResult(req, res, result);
    }
}

// Command set 

class FriendsCommandSet extends CommandSet {
    private _service: HelloFriendService;

    public constructor(controller: HelloFriendService) {
        super();

        this._service = controller;

        this.addCommand(this.makeGreeting());
    }

    private makeGreeting(): ICommand {
        return new Command('greeeting', 
            new ObjectSchema(true).withRequiredProperty('name', TypeCode.String), 
            async (ctx: IContext, args: Parameters) =>
            {
                let name = args.getAsString("name");
                let res = this._service.greeting(name);
                return res;
            }
        );
    }
}

// Commandable REST Controller (Swagger UI automatically generated from command set)

class FriendCommandableHttpController1 extends CommandableHttpController {
    private _swaggerPath: string;

    public constructor() {
        super("commandable_hello_friend1");
        this._dependencyResolver.put('service', new Descriptor("hello-friend", "service", "*", "*", "*"));
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // Swagger
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public register(): void {
        super.register();

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }
}


// Commandable REST controller (Swagger UI generated from YAML file)  

class FriendCommandableHttpController2 extends CommandableHttpController {
    private _swaggerPath: string;

    public constructor() {
        super("commandable_hello_friend2");
        this._dependencyResolver.put('service', new Descriptor("hello-friend", "service", "*", "*", "*"));
    }

    public configure(config: ConfigParams): void {
        super.configure(config);

        // Swagger
        this._swaggerPath = config.getAsNullableString("swagger.path");
    }

    public register(): void {
        super.register();

        if (this._swaggerPath != null)
            this.registerOpenApiSpecFromFile(this._swaggerPath);
    }
}

// Servcie

class HelloFriendService implements IConfigurable, ICommand {
    private defaultName: string;
    private commandSet: FriendsCommandSet;

    public constructor() {
        this.defaultName = "Pip User";
    }
    
    public configure(config: ConfigParams): void {
        this.defaultName = config.getAsStringWithDefault("default_name", this.defaultName);
    }

    public getCommandSet(): CommandSet {
        if (this.commandSet == null)
            this.commandSet = new FriendsCommandSet(this);

        return this.commandSet;
    }

    public greeting(name: string): string {
        return "Hello " + name ?? this.defaultName + " !"; 
    }
}


// Factory

class HelloFriendServiceFactory extends Factory {
    public constructor()
    {
        super();
        
        let HttpControllerDescriptor = new Descriptor("hello-friend", "controller", "http", "*", "1.0");                          // controller 1
        let CommandableHttpControllerDescriptor1 = new Descriptor("hello-friend", "controller", "commandable-http1", "*", "1.0"); // controller 2
        let CommandableHttpControllerDescriptor2 = new Descriptor("hello-friend", "controller", "commandable-http2", "*", "1.0"); // controller 2
        let ServiceDescriptor = new Descriptor("hello-friend", "service", "default", "*", "1.0");                     // Service

        this.registerAsType(HttpControllerDescriptor, HelloFriendRestController);                      // View 1
        this.registerAsType(CommandableHttpControllerDescriptor1, FriendCommandableHttpController1);   // View 2
        this.registerAsType(CommandableHttpControllerDescriptor2, FriendCommandableHttpController2);   // View 3
        this.registerAsType(ServiceDescriptor, HelloFriendService);                        // service
    }
}

// Container

class HelloFriendProcess extends ProcessContainer {
    public constructor() {
        super("hello-friend", "HelloFriend microservice");

        this._configPath = "./config.yml";
        this._factories.add(new HelloFriendServiceFactory());
        this._factories.add(new DefaultHttpFactory());
        this._factories.add(new DefaultSwaggerFactory());
    }
}


Not available
Not available
Not available
from pip_services4_components.refer import Descriptor
from pip_services4_data.validate import Schema
from pip_services4_http.controller import RestController
from pip_services4_rpc.commands import Command, CommandSet, ICommand
from pip_services4_components.exec import Parameters
from pip_services4_data.validate import Schema, ObjectSchema
from pip_services4_commons.convert import TypeCode
from pip_services4_http.controller import CommandableHttpController
from pip_services4_container.container import ProcessContainer
from pip_services4_http.build import DefaultRpcFactory
from pip_services4_swagger.build.DefaultSwaggerFactory import DefaultSwaggerFactory         
from pip_services4_rpc.commands import ICommandable
from pip_services4_components.config import IConfigurable
from pip_services4_components.refer import Descriptor
from pip_services4_components.build import Factory
from pip_services4_components.context import IContext
from typing import Optional

import bottle

# REST controller (Swagger UI from YAML file)

class HelloFriendRestController(RestController):

    def __init__(self):
        super(HelloFriendRestController, self).__init__()

        self._base_route = "/hello_friend"

        ServiceDescriptor = Descriptor('hello-friend', 'service', '*', '*', '1.0')
        self._dependency_resolver.put('service', ServiceDescriptor)
        self._controller = None

        # Swagger
        self._swagger_content = None
        self._swagger_path = None

    def configure(self, config):
        super().configure(config)
        
        # Swagger
        self._swagger_content = config.get_as_nullable_string("swagger.content")
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def set_references(self, references):
        super(HelloFriendRestController, self).set_references(references)
        self._service = self._dependency_resolver.get_one_required('service')

    def register(self):
        self.register_route(method="GET", route="/greeting", schema=Schema(), handler=self.greeting)
        
        # Swagger
        if self._swagger_content:
            self._register_open_api_spec(self._swagger_content)

        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)

    def greeting(self):
        name = bottle.request.query.get('name')
        result = self._service.greeting(name)
        return self.send_result(result)

# Command set    

class FriendsCommandSet(CommandSet):
    _service: 'HelloFriendService'

    def __init__(self, service):
        super().__init__()

        self._service = service
        self.add_command(self._make_greeting())

    def _make_greeting(self) -> ICommand:
        def handler(context: Optional[IContext], args: Parameters):
            name = args.get_as_string("name")
            res = self._service.greeting(name)
            return res

        return Command(
            "greeting",
            ObjectSchema(True).with_required_property("name", TypeCode.String),
            handler
        )
    
# Commandable REST controller (Swagger UI automatically generated from command set)

class FriendCommandableHttpController1(CommandableHttpController):

    def __init__(self):
        super().__init__('commandable_hello_friend1')
        self._dependency_resolver.put('service', Descriptor('hello-friend', 'service', '*', '*', '*'))

        self._swagger_path = None
        
    def configure(self, config):
        super().configure(config)

        # Swagger
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def register(self):
        super().register()
        
        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)

# Commandable REST controller (Swagger UI generated from YAML file)            

class FriendCommandableHttpController2(CommandableHttpController):

    def __init__(self):
        super().__init__('commandable_hello_friend2')
        self._dependency_resolver.put('service', Descriptor('hello-friend', 'service', '*', '*', '*'))

        self._swagger_path = None
        
    def configure(self, config):
        super().configure(config)

        # Swagger
        self._swagger_path = config.get_as_nullable_string('swagger.path')

    def register(self):
        super().register()

        if self._swagger_path:
            self._register_open_api_spec_from_file(self._swagger_path)
            
# Service

class HelloFriendService(IConfigurable, ICommandable): 
                                          
    __defaultName = None
    __command_set: 'FriendsCommandSet' = None
        
    def __init__(self):
        self.__defaultName = "Pip User"

    def configure(self, config):
        self.__defaultName = config.get_as_string_with_default("default_name", self.__defaultName)
        
    def get_command_set(self) -> CommandSet:
        if self.__command_set is None:
            self.__command_set = FriendsCommandSet(self)

        return self.__command_set

    def greeting(self, name):
        return f"Hello, {name if name else self.__defaultName} !"

# Factory

class HelloFriendControllerFactory(Factory):
    def __init__(self):
        super(HelloFriendControllerFactory, self).__init__()

        HttpControllerDescriptor = Descriptor('hello-friend', 'controller', 'http', '*', '1.0')                          # Controller
        CommandableHttpControllerDescriptor1 = Descriptor('hello-friend', 'controller', 'commandable-http1', '*', '1.0') # Controller
        CommandableHttpControllerDescriptor2 = Descriptor('hello-friend', 'controller', 'commandable-http2', '*', '1.0') # Controller
        ServiceDescriptor = Descriptor('hello-friend', 'service', 'default', '*', '1.0')                     # Service
                                                                                
            
        self.register_as_type(HttpControllerDescriptor, HelloFriendRestController)                       # Controler
        self.register_as_type(CommandableHttpControllerDescriptor1, FriendCommandableHttpController1)    # Controller
        self.register_as_type(CommandableHttpControllerDescriptor2, FriendCommandableHttpController2)    # Controller
        self.register_as_type(ServiceDescriptor, HelloFriendService)                         # Service
        
# Container

class HelloFriendProcess(ProcessContainer):

    def __init__(self):
        super(HelloFriendProcess, self).__init__('hello-friend', 'HelloFriend microservice')
        self._config_path = './config35.yaml'
        self._factories.add(HelloFriendControllerFactory())
        self._factories.add(DefaultRpcFactory())
        self._factories.add(DefaultSwaggerFactory())
        
# Runner        

if __name__ == '__main__':
    runner = HelloFriendProcess()
    print("run")
    try:
        runner.run()
    except Exception as ex:
        print(ex)


Not available
config.yaml
---
# Container context
- descriptor: "pip-services:context-info:default:default:1.0"
  name: "hello-friend"
  description: "HelloFriend microservice"

# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
  level: "trace"

# Performance counter that post values to log
- descriptor: "pip-services:counters:log:default:1.0"

# Service
- descriptor: "hello-friend:service:default:default:1.0"
  default_name: "Friend"

# Shared HTTP Endpoint
- descriptor: "pip-services:endpoint:http:default:1.0"
  connection:
    protocol: http
    host: 0.0.0.0
    port: 8080

# HTTP controller V1
- descriptor: "hello-friend:controller:http:default:1.0"
  swagger:
    enable: true
    path: './rest_swagger.yml'


- descriptor: "hello-friend:controller:commandable-http1:default:1.0"
  swagger:
    enable: true
    auto: true
    route: swagger
    name: Friends Service
    description: Commandable REST API - Automatic
- descriptor: "hello-friend:controller:commandable-http2:default:1.0"
  swagger:
    enable: true
    auto: false
    route: swagger
    path: './commandable_swagger.yml'

# Heartbeat controller
- descriptor: "pip-services:heartbeat-controller:http:default:1.0"

# Status controller
- descriptor: "pip-services:status-controller:http:default:1.0"

# Swagger controller
- descriptor: "pip-services:swagger-controller:http:default:1.0"
Not available
Not available
Not available
---
# Container context
- descriptor: "pip-services:context-info:default:default:1.0"
  name: "hello-friend"
  description: "HelloFriend microservice"

# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
  level: "trace"

# Performance counter that post values to log
- descriptor: "pip-services:counters:log:default:1.0"

# Service
- descriptor: "hello-friend:service:default:default:1.0"
  default_name: "Friend"

# Shared HTTP Endpoint
- descriptor: "pip-services:endpoint:http:default:1.0"
  connection:
    protocol: http
    host: 0.0.0.0
    port: 8080

# HTTP controller V1
- descriptor: "hello-friend:controller:http:default:1.0"
  swagger:
    enable: true
    path: './rest_swagger.yml'


- descriptor: "hello-friend:controller:commandable-http1:default:1.0"
  swagger:
    enable: true
    auto: true
    route: swagger
    name: Friends Service
    description: Commandable REST API - Automatic
- descriptor: "hello-friend:controller:commandable-http2:default:1.0"
  swagger:
    enable: true
    auto: false
    route: swagger
    path: './commandable_swagger.yml'

# Heartbeat controller
- descriptor: "pip-services:heartbeat-controller:http:default:1.0"

# Status controller
- descriptor: "pip-services:status-controller:http:default:1.0"

# Swagger controller
- descriptor: "pip-services:swagger-controller:http:default:1.0"
Not available

Wrapping up

In this tutorial, we have seen how to create Swagger UIs from a REST controller and a commandable REST controller. First, we created a REST controller that is Swagger enabled and obtained all the information necessary to create the UI from a YAML file. After that, we created a commandable REST controller, which developed a UI from a set of commands or a YAML file. Finally, we created a process container used to run our app. Once run, our app produced Swagger UIs documenting the greeting method for each case.