Step 6. Controllers and versioning

A facade’s RESTful operations are implemented by REST controllers, which receive external requests and delegate their execution to operations.

To process requests made to the API’s first version, let’s create a file named FacadeControllerV1.py in the services/version1 folder with the following code:

/src/services/version1/FacadeServiceV1.ts

import { IReferences } from 'pip-services4-components-node';
import { ConfigParams } from 'pip-services4-components-node';
import { RestController } from 'pip-services4-http-node';
import { AboutOperations } from 'pip-services4-http-node';

import { AuthorizerV1 } from '../version1/AuthorizerV1';
import { SessionsOperationsV1 } from '../../operations/version1/SessionsOperationsV1';
import { SitesOperationsV1 } from '../../operations/version1/SitesOperationsV1';
import { InvitationsOperationsV1 } from '../../operations/version1/InvitationsOperationsV1';
import { BeaconsOperationsV1 } from '../../operations/version1/BeaconsOperationsV1';

export class FacadeServiceV1 extends RestController {
    private _aboutOperations = new AboutOperations();
    private _sessionsOperations = new SessionsOperationsV1();
    private _sitesOperations = new SitesOperationsV1();
    private _invitationsOperations = new InvitationsOperationsV1();
    private _beaconsOperations = new BeaconsOperationsV1();

    public constructor() {
        super();
        this._baseRoute = "api/v1"
    }

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

        this._aboutOperations.configure(config);
        this._sessionsOperations.configure(config);
        this._sitesOperations.configure(config);
        this._invitationsOperations.configure(config);
        this._beaconsOperations.configure(config);
    }

    public setReferences(references: IReferences): void {
        super.setReferences(references);
        
        this._aboutOperations.setReferences(references);
        this._sessionsOperations.setReferences(references);
        this._sitesOperations.setReferences(references);
        this._invitationsOperations.setReferences(references);
        this._beaconsOperations.setReferences(references);
    }   

    public register(): void {
        let auth = new AuthorizerV1();

        // Restore session middleware
        this.registerInterceptor('',
            (req, res, next) => { this._sessionsOperations.loadSession(req, res, next); });

        // About Route
        this.registerRouteWithAuth('get', '/about', null, auth.anybody(),
            (req, res) => { this._aboutOperations.about(req, res); });

        // Session Routes
        this.registerRouteWithAuth('post', '/signup', null, auth.anybody(),
            (req, res) => { this._sessionsOperations.signup(req, res); });
        this.registerRouteWithAuth('get', '/signup/validate', null, auth.anybody(),
            (req, res) => { this._sessionsOperations.signupValidate(req, res); });
        this.registerRouteWithAuth('post', '/signin', null, auth.anybody(),
            (req, res) => { this._sessionsOperations.signin(req, res); });
        this.registerRouteWithAuth('post', '/signout', null, auth.anybody(),
            (req, res) => { this._sessionsOperations.signout(req, res); });
        this.registerRouteWithAuth('get', '/sessions', null, auth.admin(),
            (req, res) => { this._sessionsOperations.getSessions(req, res); });
        this.registerRouteWithAuth('post', '/sessions/restore', null, auth.signed(),
            (req, res) => { this._sessionsOperations.restoreSession(req, res); });
        this.registerRouteWithAuth('get', '/sessions/current', null, auth.signed(),
            (req, res) => { this._sessionsOperations.getCurrentSession(req, res); });
        this.registerRouteWithAuth('get', '/sessions/:user_id', null, auth.ownerOrAdmin('user_id'),
            (req, res) => { this._sessionsOperations.getUserSessions(req, res); });
        this.registerRouteWithAuth('del', '/sessions/:user_id/:session_id', null, auth.ownerOrAdmin('user_id'),
            (req, res) => { this._sessionsOperations.closeSession(req, res); });

        // Site Routes
        this.registerRouteWithAuth('get', '/sites', null, auth.signed(),
            (req, res) => { this._sitesOperations.getAuthorizedSites(req, res); });
        this.registerRouteWithAuth('get', '/sites/all', null, auth.admin(),
            (req, res) => { this._sitesOperations.getSites(req, res); });
        this.registerRouteWithAuth('get', '/sites/find_by_code', null, auth.anybody(),
            (req, res) => { this._sitesOperations.findSiteByCode(req, res); });
        this.registerRouteWithAuth('get', '/sites/:site_id', null, auth.siteUser(),
            (req, res) => { this._sitesOperations.getSite(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/generate_code', null, auth.siteAdmin(),
            (req, res) => { this._sitesOperations.generateCode(req, res); });
        this.registerRouteWithAuth('post', '/sites', null, auth.signed(),
            (req, res) => { this._sitesOperations.createSite(req, res); });
        this.registerRouteWithAuth('post', '/sites/validate_code', null, auth.signed(),
            (req, res) => { this._sitesOperations.validateSiteCode(req, res); });
        this.registerRouteWithAuth('put', '/sites/:site_id', null, auth.siteAdmin(),
            (req, res) => { this._sitesOperations.updateSite(req, res); });
        this.registerRouteWithAuth('del', '/sites/:site_id', null, auth.admin(),
            (req, res) => { this._sitesOperations.deleteSite(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/remove', null, auth.siteUser(),
            (req, res) => { this._sitesOperations.removeSite(req, res); });

        // Invitation Routes
        this.registerRouteWithAuth('get', '/sites/:site_id/invitations', null, auth.siteUser(),
            (req, res) => { this._invitationsOperations.getInvitations(req, res); });
        this.registerRouteWithAuth('get', '/sites/:site_id/invitations/:invitation_id', null, auth.siteUser(),
            (req, res) => { this._invitationsOperations.getInvitation(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/invitations', null, auth.signed(),
            (req, res) => { this._invitationsOperations.sendInvitation(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/invitations/notify', null, auth.siteManager(),
            (req, res) => { this._invitationsOperations.notifyInvitation(req, res); });
        this.registerRouteWithAuth('del', '/sites/:site_id/invitations/:invitation_id', null, auth.siteManager(),
            (req, res) => { this._invitationsOperations.deleteInvitation(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/invitations/:invitation_id/approve', null, auth.siteManager(),
            (req, res) => { this._invitationsOperations.approveInvitation(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/invitations/:invitation_id/deny', null, auth.siteManager(),
            (req, res) => { this._invitationsOperations.denyInvitation(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/invitations/:invitation_id/resend', null, auth.siteManager(),
            (req, res) => { this._invitationsOperations.resendInvitation(req, res); });

        // Beacon Routes
        this.registerRouteWithAuth('get', '/sites/:site_id/beacons', null, auth.siteUser(),
            (req, res) => { this._beaconsOperations.getBeacons(req, res); });
        this.registerRouteWithAuth('get', '/sites/:site_id/beacons/:beacon_id', null, auth.siteUser(),
            (req, res) => { this._beaconsOperations.getBeacon(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/beacons/calculate_position', null, auth.siteManager(),
            (req, res) => { this._beaconsOperations.calculatePosition(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/beacons', null, auth.siteManager(),
            (req, res) => { this._beaconsOperations.createBeacon(req, res); });
        this.registerRouteWithAuth('post', '/sites/:site_id/beacons/validate_udi', null, auth.signed(),
            (req, res) => { this._beaconsOperations.validateBeaconUdi(req, res); });
        this.registerRouteWithAuth('put', '/sites/:site_id/beacons/:beacon_id', null, auth.siteManager(),
            (req, res) => { this._beaconsOperations.updateBeacon(req, res); });
        this.registerRouteWithAuth('del', '/sites/:site_id/beacons/:beacon_id', null, auth.siteManager(),
            (req, res) => { this._beaconsOperations.deleteBeacon(req, res); });
    }
}


/services/version1/FacadeControllerV1.go

package controllers1

import (
	"context"
	"net/http"

	operations1 "github.com/pip-services-samples/pip-samples-facade-go/operations/version1"
	cconf "github.com/pip-services4/pip-services4-go/pip-services4-components-go/config"
	cref "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
	httpcontr "github.com/pip-services4/pip-services4-go/pip-services4-http-go/controllers"
)

type FacadeServiceV1 struct {
	*httpcontr.RestController
	sessionsOperations *operations1.SessionsOperationsV1
	beaconsOperations  *operations1.BeaconsOperationsV1
}

func NewFacadeServiceV1() *FacadeServiceV1 {
	c := &FacadeServiceV1{
		sessionsOperations: operations1.NewSessionsOperationsV1(context.Background()),
		beaconsOperations:  operations1.NewBeaconsOperationsV1(),
	}
	c.RestController = httpcontr.InheritRestController(c)
	c.BaseRoute = "api/v1"
	return c
}

func (c *FacadeServiceV1) Configure(config *cconf.ConfigParams) {
	c.RestController.Configure(context.Background(), config)

	c.sessionsOperations.Configure(context.Background(), config)
	c.beaconsOperations.Configure(context.Background(), config)
}

func (c *FacadeServiceV1) SetReferences(references cref.IReferences) {
	c.RestController.SetReferences(context.Background(), references)

	c.sessionsOperations.SetReferences(context.Background(), references)
	c.beaconsOperations.SetReferences(references)
}

func (c *FacadeServiceV1) Register() {
	auth := NewAuthorizerV1()

	// Restore session middleware
	c.RegisterInterceptor("",
		func(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
			c.sessionsOperations.LoadSession(res, req, next)
		})

	c.registerContentManagementRoutes(auth)
	c.registerUsersRoutes(auth)
}

func (c *FacadeServiceV1) registerContentManagementRoutes(auth *AuthorizerV1) {
	// Beacons routes
	c.RegisterRouteWithAuth("get", "/beacons", nil, auth.Signed(),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.GetBeacons(res, req) })
	c.RegisterRouteWithAuth("get", "/beacons/{id}", nil, auth.Owner("user_id"),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.GetBeaconById(res, req) })
	c.RegisterRouteWithAuth("get", "/beacons/udi/{udi}", nil, auth.Owner(""),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.GetBeaconByUdi(res, req) })
	c.RegisterRouteWithAuth("post", "/beacons", nil, auth.Signed(),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.CreateBeacon(res, req) })
	c.RegisterRouteWithAuth("put", "/beacons", nil, auth.Signed(),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.UpdateBeacon(res, req) })
	c.RegisterRouteWithAuth("delete", "/beacons/{id}", nil, auth.Signed(),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.DeleteBeaconById(res, req) })
	c.RegisterRouteWithAuth("post", "/beacons/position", nil, auth.Signed(),
		func(res http.ResponseWriter, req *http.Request) { c.beaconsOperations.CalculatePosition(res, req) })
}

func (c *FacadeServiceV1) registerUsersRoutes(auth *AuthorizerV1) {
	// Session Routes
	c.RegisterRouteWithAuth("post", "/users/signup", nil, auth.Anybody(),
		func(res http.ResponseWriter, req *http.Request) { c.sessionsOperations.Signup(res, req) })
	c.RegisterRouteWithAuth("post", "/users/signin", nil, auth.Anybody(),
		func(res http.ResponseWriter, req *http.Request) { c.sessionsOperations.Signin(res, req) })
	c.RegisterRouteWithAuth("post", "/users/signout", nil, auth.Anybody(),
		func(res http.ResponseWriter, req *http.Request) { c.sessionsOperations.Signout(res, req) })
}

Not available

/pip_facades_sample_python/controllers/version1/FacadeControllerV1.py

# -*- coding: utf-8 -*-
from pip_services4_components.config import ConfigParams
from pip_services4_components.refer import IReferences
from pip_services4_http.controller import AboutOperations,RestController

from pip_facades_sample_python.operations.version1.BeaconsOperationsV1 import BeaconsOperationsV1
from pip_facades_sample_python.operations.version1.InvitationsOperationsV1 import InvitationsOperationsV1
from pip_facades_sample_python.operations.version1.SessionsOperationsV1 import SessionsOperationsV1
from pip_facades_sample_python.operations.version1.SitesOperationsV1 import SitesOperationsV1
from pip_facades_sample_python.controllers.version1.AuthorizerV1 import AuthorizerV1


class FacadeControllerV1(RestController):

    def __init__(self):
        super(FacadeControllersV1, self).__init__()
        self._base_route = 'api/v1'

        self.__about_operations = AboutOperations()
        self.__session_operations = SessionsOperationsV1()
        self.__sites_operations = SitesOperationsV1()
        self.__invitations_operations = InvitationsOperationsV1()
        self.__beacons_operations = BeaconsOperationsV1()

    def configure(self, config: ConfigParams):
        super().configure(config)

        self.__about_operations.configure(config)
        self.__session_operations.configure(config)
        self.__sites_operations.configure(config)
        self.__invitations_operations.configure(config)
        self.__beacons_operations.configure(config)

    def set_references(self, references: IReferences):
        super().set_references(references)

        self.__about_operations.set_references(references)
        self.__session_operations.set_references(references)
        self.__sites_operations.set_references(references)
        self.__invitations_operations.set_references(references)
        self.__beacons_operations.set_references(references)

    def register(self):
        auth = AuthorizerV1()

        # Restore session middleware
        self.register_interceptor('', lambda: self.__session_operations.load_session())

        self.register_route_with_auth('get', '/about', None, auth.anybody(),
                                      lambda: self.__about_operations.get_about())
        # Session Routes
        self.register_route_with_auth('post', '/signup', None, auth.anybody(),
                                      lambda: self.__session_operations.signup())
        self.register_route_with_auth('get', '/signup/validate', None, auth.anybody(),
                                      lambda: self.__session_operations.signup_validate())
        self.register_route_with_auth('post', '/signin', None, auth.anybody(),
                                      lambda: self.__session_operations.signin())
        self.register_route_with_auth('post', '/signout', None, auth.anybody(),
                                      lambda: self.__session_operations.signout())
        self.register_route_with_auth('get', '/sessions', None, auth.admin(),
                                      lambda: self.__session_operations.get_sessions())
        self.register_route_with_auth('post', '/sessions/restore', None, auth.signed(),
                                      lambda: self.__session_operations.restore_session())
        self.register_route_with_auth('get', '/sessions/current', None, auth.signed(),
                                      lambda: self.__session_operations.get_current_session())
        self.register_route_with_auth('get', '/sessions/:user_id', None, auth.owner_or_admin('user_id'),
                                      lambda user_id: self.__session_operations.get_user_sessions(user_id))
        self.register_route_with_auth('del', '/sessions/:user_id/:session_id', None, auth.owner_or_admin('user_id'),
                                      lambda user_id, session_id: self.__session_operations.close_session(user_id,
                                                                                                          session_id))

        # Site Routes
        self.register_route_with_auth('get', '/sites', None, auth.signed(),
                                      lambda: self.__sites_operations.get_authorized_sites())
        self.register_route_with_auth('get', '/sites/all', None, auth.admin(),
                                      lambda: self.__sites_operations.get_sites())
        self.register_route_with_auth('get', '/sites/find_by_code', None, auth.anybody(),
                                      lambda: self.__sites_operations.find_site_by_code())
        self.register_route_with_auth('get', '/sites/:site_id', None, auth.site_user(),
                                      lambda site_id: self.__sites_operations.get_site(site_id))
        self.register_route_with_auth('post', '/sites/:site_id/generate_code', None, auth.site_admin(),
                                      lambda site_id: self.__sites_operations.generate_code(site_id))
        self.register_route_with_auth('post', '/sites', None, auth.signed(),
                                      lambda: self.__sites_operations.create_site())
        self.register_route_with_auth('post', '/sites/validate_code', None, auth.signed(),
                                      lambda: self.__sites_operations.validate_site_code())
        self.register_route_with_auth('put', '/sites/:site_id', None, auth.site_admin(),
                                      lambda site_id: self.__sites_operations.update_site(site_id))
        self.register_route_with_auth('delete', '/sites/:site_id', None, auth.admin(),
                                      lambda site_id: self.__sites_operations.delete_site(site_id))
        self.register_route_with_auth('post', '/sites/:site_id/remove', None, auth.site_user(),
                                      lambda site_id: self.__sites_operations.remove_site(site_id))

        # Invitation Routes
        self.register_route_with_auth('get', '/sites/:site_id/invitations', None, auth.site_user(),
                                      lambda site_id: self.__invitations_operations.get_invitations(site_id))
        self.register_route_with_auth('get', '/sites/:site_id/invitations/:invitation_id', None, auth.site_user(),
                                      lambda site_id, invitation_id: self.__invitations_operations.get_invitation(
                                          site_id, invitation_id))
        self.register_route_with_auth('post', '/sites/:site_id/invitations', None, auth.signed(),
                                      lambda site_id: self.__invitations_operations.send_invitation(site_id))
        self.register_route_with_auth('post', '/sites/:site_id/invitations/notify', None, auth.site_manager(),
                                      lambda site_id: self.__invitations_operations.notify_invitation(site_id))
        self.register_route_with_auth('delete', '/sites/:site_id/invitations/:invitation_id', None, auth.site_manager(),
                                      lambda site_id, invitation_id: self.__invitations_operations.delete_invitation(
                                          site_id, invitation_id))
        self.register_route_with_auth('post', '/sites/:site_id/invitations/:invitation_id/approve', None,
                                      auth.site_manager(),
                                      lambda site_id, invitation_id: self.__invitations_operations.approve_invitation(
                                          site_id, invitation_id))
        self.register_route_with_auth('post', '/sites/:site_id/invitations/:invitation_id/deny', None,
                                      auth.site_manager(),
                                      lambda site_id, invitation_id: self.__invitations_operations.deny_invitation(
                                          site_id, invitation_id))
        self.register_route_with_auth('post', '/sites/:site_id/invitations/:invitation_id/resend', None,
                                      auth.site_manager(),
                                      lambda site_id, invitation_id: self.__invitations_operations.resend_invitation(
                                          site_id, invitation_id))

        # Beacon Routes
        self.register_route_with_auth('get', '/sites/:site_id/beacons', None, auth.site_user(),
                                      lambda site_id: self.__beacons_operations.get_beacons(site_id))
        self.register_route_with_auth('get', '/sites/:site_id/beacons/:beacon_id', None, auth.site_user(),
                                      lambda site_id, beacon_id: self.__beacons_operations.get_beacon(site_id,
                                                                                                      beacon_id))
        self.register_route_with_auth('post', '/sites/:site_id/beacons/calculate_position', None, auth.site_user(),
                                      lambda site_id: self.__beacons_operations.calculate_position(site_id))
        self.register_route_with_auth('post', '/sites/:site_id/beacons', None, auth.site_user(),
                                      lambda site_id: self.__beacons_operations.create_beacon(site_id))
        self.register_route_with_auth('post', '/sites/:site_id/beacons/validate_udi', None, auth.signed(),
                                      lambda site_id: self.__beacons_operations.validate_beacon_udi(site_id))
        self.register_route_with_auth('put', '/sites/:site_id/beacons/:beacon_id', None, auth.site_user(),
                                      lambda site_id, beacon_id: self.__beacons_operations.update_beacon(site_id,
                                                                                                         beacon_id))
        self.register_route_with_auth('delete', '/sites/:site_id/beacons/:beacon_id', None, auth.site_user(),
                                      lambda site_id, beacon_id: self.__beacons_operations.delete_beacon(site_id,
                                                                                                         beacon_id))
Not available

The FacadeControllerV1 component extends the standard RestController, which implements the majority of the component’s basic functionality. All that we have left to do is define some routes and delegate their processing to the operations we defined in the previous steps.

A base route is defined in the constructor, which contains the API version that is implemented in the controller.

In the register method, before we register our routes, we make sure to register the load_session interceptor, which will be loading user sessions using the parameters set in the REST requests.

Next, the routes are registered, access limits are set up using our Authorizer, and request handlers are defined using the business methods we implemented in our REST operation sets. For the sake of convenience, the registration functions are divided into 2 groups, each of which belongs to a specific operation-component.

When implementing a new version of the API, the following must be done:

  1. Implement new REST operations or modify a copy of the existing ones;
  2. Create a new FacadeServiceVx and set a new “/api/vx” base route;
  3. Register routes for the new API and delegate their processing to the newly added and to already existing REST operations.

Continue on to Step 7 - Testing of Operations to see how we can automate the testing of the controller and operations we’ve created, including the authentication and authorization of requests.

Step 7 - Testing of Operations