# Version 14

## Index & Change log
* [Using the API Controller](#using-the-api-controller)
    * [Instantiation and Config class](#instantiate)
    * [API Registry](#api-registry)
    * [API Calls in Toto](#api-calls)
    * [Register API Paths](#register-api-paths)
    * [Register an Event Handler](#register-an-event-handler)
    * [Start your api](#start)

Version 14 introduces a few fundamental chenges: 

1. **Event Handling**: event handlers are now registered separately than a normal REST endpoint. <br>
    Support for: 
    * AWS SNS
    * GCP PubSub

2. **Toto Token**: it is now possible to generate a JWT Token for a backend service, instead of relying on propagating tokens. <br>
    This choice has been made to support scenarios where a service is called without a token (e.g. SNS HTTPS push endpoints).<br>
    It is also more correct, for event handlers that need to call other Toto Services to use their own token (identity) rather than propagating something like a PubSub Service Account.

3. **Toto Controller Config is now an Abstract Class**. <br>
    Now you will need to `extend TotoControllerConfig` instead of using `implement` as it is no longer an interface but an abstract class.<br>
    That was done to avoid boilerplate code to be rewritten all the time in every implementation of a Toto Microservice.

4. **Toto API Registry**. <br>
    This version (starting at 14.1.0) introduces an **API Registry** (see repo [Toto API Registry](https://github.com/nicolasances/toto-ms-registry)). <br>
    The goal of the registry is to provide endpoints for Toto APIs, which avoids having to store them in secrets or having to inject them in all microservices. <br>

5. **Toto API Calls**. <br>
    There is now a simplified way to call other APIs in the Toto ecosystem: through [TotoAPI](#api-calls-in-toto), which takes care of all the boilerplate of auth tokens, endpoints search, etc.. 

6. **Automated use of Service Tokens**. <br>
    When using `TotoAPI`, the auth token is automatically created for the service. That avoids having to always propagate a token. <br>
    In case you want to propagate a token you can still do it through the configuration of the TotoAPI.

<br>

---
## Using the API Controller
The API Controller can be used as follows: 

### Import
Import through `import { TotoAPIController } from "toto-api-controller";`

---
### Instantiate
Instantiate through 
```
const api = new TotoAPIController(new ControllerConfig({apiName: "tome-ms-xxx"}, options), {basePath: '/xxx'});
```

The term `options` allows to define the following properties: 
```
TotoControllerConfigOptions {
    defaultHyperscaler: "aws" | "gcp" = "gcp";
    defaultSecretsManagerLocation: "aws" | "gcp" = "gcp";
}
```
where: 
 * `defaultHyperscaler` is the hyperscaler to be used **in case none is found in the `HYPERSCALER` environment variable**
 * `defaultSecretsManagerLocation` is the hyperscaler to be used in case `HYPERSCALER` is `local` (you're running locally) and you want to use an hyperscaler's Secret Manager to load the default secrets (see below).<br>
 This configuration is **ONLY USED TO FIGURE OUT WHERE TO LOOK FOR A SECRETS MANAGER**. 

The instantiation requires that you have defined a `ControllerConfig` class that `extends TotoControllerConfig`.<br>
The base class does the following: 
* Loads mandatory secrets: 
    * `mongo-host` - the host of the environment's Mongo instance
    * `jwt-signing-key` - the key used to generate tokens
    * `toto-expected-audience` - the audience expected in JWT tokens received by the Microservice
* Creates the following instance variables, that can be used anywhere in the code that has access to the Config class
    * `hyperscaler`
    * `env`

This class will thus have to define the following methods:

 * `async load()` that loads any configuration needed (e.g. secrets). <br>
    **Important**: the `load()` method is implemented in the base class. If you override it, remember to call the base class implementation as follows: 
    
```
    // My Controller Config class
    async load(): Promise<any> {
        
        let promises = [];

        // THIS IS VERY IMPORTANT as the base class loads some mandatory secrets from the environment (jwt signing key, etc..)
        promises.push(super.load());

        promises.push( ... my async stuff ..);

        await Promise.all(promises);

    }
```

 * `getProps(): ValidatorProps` that returns the following properties, **all optional**: 
    * `noAuth`: default false, if set to true, the service will bypass the verification of the Authorization header. This means that the API will be **unauthenticated**. 
    * `noCorrelation`: default false, if set to true, the caller won't have to pass a `x-correlation-id` header
    * `minAppVersion`: default null, if set to a value, the caller will have to provide a `x-app-version` header and the API Controller will verify that the provided header value is greater or equal than this `minAppVersion`
    * `customAuthProvider`: in case a custom auth provider is used (e.g. Toto), the name of the authProvider should be used. This name **must** match the content of the `authProvider` field of the JWT Token provided when interacting with the API.
 
You can **specify a base path** to be **prepended** to your routes: 
```
const api = new TotoAPIController("toto-ms-ex1", new ControllerConfig(), { basePath: '/ex1' });
```
In the example above, all path registrations using the `api.path()` method will prepend `/ex1` to the path. <br>
*Example: specifying the base path `/pippo` will mean that when you register a path, e.g. `api.path('GET', '/hello', ...)` the path will **not** be available under `<ms-endpoint>/hello` but under `<ms-endpoint>/pippo/hello`. <br>
**Note** that you can **avoid** this behaviour on a per-path basis, see below.

--- 
### API Registry
You can now always retrieve Toto APIs' endpoints through the Toto Registry API. <br>

The Registry works in the following way: 

1. At startup, a Toto API that uses this API Controller will register itself to the registry. 

2. Whenever you want the endpoint of a Toto API, you can call the `RegistryCache` to retrieve it: <br>
    `await RegistryCache.getInstance().getEndpoint(apiName);`<br>
    * The `apiName` is the logical name of the api (typically something like `toto-ms-xx`) as registered in the Toto API Registry.

But you most likely won't need to use the Registry directly, and can instead use the following [API Calls in Toto](#api-calls-in-toto)

---
### API Calls in Toto
Whenever you'd like to integrate your service with other Toto microservices, you can create a class that extends 'TotoAPI' as follows (in this example the 'tome-ms-topics' API). 

'''
export class TomeTopicsAPI extends TotoAPI {

    /**
     * Retrieves a topic by its id
     * @param topicId the id of the topic
     * @param cid cid
     * @returns the topic response
     */
    async getTopic(topicId: string, cid?: string): Promise<GetTopicResponse> {
        return this.get(new TotoAPIRequest(`/topics/${topicId}`, cid), GetTopicResponse);
    }

}
'''

The Endpoint of the API you're trying to reach is automatically found using the **Toto API Registry**.

You need to provide a Response class that provides a static 'fromParsedHTTPResponseBody()' method in order to parse the output. In this case, the 'GetTopicResponse' takes care of that: 

'''
class GetTopicResponse {

    id: string = "";
    name: string = "";
    topicCode: string = ""; 
    sections: string[] = [];

    static fromParsedHTTPResponseBody(body: any): GetTopicResponse {
        const response = new GetTopicResponse();
        response.id = body.id;
        response.name = body.name;
        response.topicCode = body.topicCode;
        response.sections = body.sections;
        return response;
    }
}
'''
---
### Register API Paths
To register an API path, add a line for each path: <br>
`api.path("GET", "/games", new GetGamesOverview())`

The `new GetGamesOverview()` instantiates a `TotoDelegate`. <br>
Toto Delegates have the responsibility to handle requests to a given path.<br>
They implement the method: <br>
`async do(req: Request, userContext: UserContext, execContext: ExecutionContext): Promise<any>`

If you have configured a base path on the controller but want a specific endpoint (path) to ignore it, you can do the following: 
`api.path('GET', '/pippo', new SmokeDelegate(), { ignoreBasePath: true });`

#### UserContext
The variable `userContext` contains the following fields:
 * `userId`: the id of the user according to the Identity Provider
 * `email`: the user email
 * `authProvider`: a string identifying the IDP

#### ExecutionContext
The variable `executionContext` contains the following: 
 * `logger`: a `TotoLogger` that can be used to log messages to the console out
 * `cid`: the correlation id
 * `appVersion`: the app version (content of the `x-app-version` header, if present)
 * `apiName`: the name of this API
 * `config`: the instance of the `TotoControllerConfig` used by this API

---

### Register an Event Handler
Registering an event handler is done differently than registering a path: 

```
api.registerPubSubEventHandler('myResource', new OnMyResourceEvent());
```
This will create a `POST /events/myResource` endpoint

* `myResource` is the name of the resource that the event handler is related to. <br>
* `OnMyResourceEvent` is the event handler

An EventHandler must implement `ITotoPubSubEventHandler`: 

```
export class OnMyResourceEvent implements ITotoPubSubEventHandler {

    async onEvent(msg: TotoMessage, execContext: ExecutionContext): Promise<any> {

        const logger = execContext.logger;
        const cid = execContext.cid;

        logger.compute(cid, `Received event ${JSON.stringify(msg)}`)

        ... do your thing

        return { consumed: true };

    }

}
```

--- 
### Start
To start the API Controller, just add this: 
```
api.init().then(() => {
    api.listen()
});
```

