Skip to content

3. Custom Extensions

Arley Triana Morin edited this page Jul 7, 2023 · 40 revisions

In the previous chapters, we learned how to create and deploy a CAP-based SaaS application.

Now, let's switch roles and put ourselves in the shoes of a SaaS customer, specifically an extension developer named bob. Bob's task is to extend the subscribed Incidents Management app to customize it according to the specific needs of customer t1.

Customer t1 is a vendor of Solar Panels, primarily targeting private homeowners. As part of the extension, Bob needs to incorporate additional information when creating incidents, such as affected components (e.g., panels, batteries, controller units, etc.), panel orientations, weather conditions, and more.

The following sections provide an overview of the SaaS Extensibility guide in capire.

Add Test Tenant for Extension

To test-drive and validate the extension before activating it in production, bob asks his administrator alice to create a test tenant. In our local setup, we simulate this process as follows:

  1. Set up a test tenant t1-ext by entering the following command in the terminal:
cds subscribe t1-ext --to http://localhost:4005 -u alice
  1. Assign extension developers for the test tenant. Since we’re using mocked auth, we can mock this step by adding the following lines to the base app’s package.json, which assigns bob as the extension developer for t1-ext:

     "cds": {
       "requires": {
         ...,
         "auth": {
           "users": {
             "bob": {
               "tenant": "t1-ext",
               "roles": ["cds.ExtensionDeveloper"]
             }
           }
         }
       }
     }
    

👉 Copy and insert the respective lines in the correct location in the package.json file. Remember to restart both the MTX sidecar and the base application before proceeding.

Start Extension Project

SaaS providers typically offer project-specific guides and templates for extensions. In our case, we have provided a project template for the incidents management sample, which can be found in the mono repo at ./x-t1 (GitHub link). For this exercise, we will open that project in a new BAS window:

  1. Open a new BAS window: Menu > File > New Window (refer to exercise 1 for detailed instructions)

  2. Open the cloned sub-project extensibility-sample/t1x (also covered in exercise 1)

  3. After completing these steps, your browser window should resemble the following:

    image-20221114063037645

An extension project is essentially a minimalistic CAP project that consists of a package.json file. In our case, the file looks like this:

image-20221114064753498

The package.json file configures the extension's package name, which can be chosen freely. The extends configuration specifies the namespace of the base application's CDS models, which we will learn more about in the next section.

Our template also includes additional files prepared by the provider of the SaaS application, which serve as starting points for actual extensions. We will explore these files further in the upcoming sections.

Pull the Latest Base Model

We need to pull the model to ensure we have the latest CDS model of the base application and benefit from tooling support for creating extensions (such as code completion and error highlighting). Follow these steps:

  1. Open a Terminal.
  2. Copy and paste the following command and run it:
cds pull --from http://localhost:4005 -u bob

This command will automatically pull the model from the test tenant t1-ext since bob is assigned to that tenant.

  1. When prompted for a password, press Enter without any input.

The base model will be downloaded and stored into ./node_modules/@capire/incidents/index.csn, corresponding to the extends configuration in our package.json.

Add Extension Fields

Now we proceed with specifying the extension by adding additional fields to the Incidents entity to capture specific properties of solar panel installations. Follow these steps:

  1. Open file app/extensions.cds. The provided template looks like this:

    using { sap.capire.incmgt.Incidents } from '@capire/incidents';
    
    /** Template for adding extension fields to incidents... */
    extend Incidents with {
    
      ext_field1 : String @title: '...';
      ext_field2 : String @title: '...' enum {
        value1 @title: '...' @description: '...';
        value2 @title: '...' @description: '...';
      };
    
    }
  2. Replace the extend section with the following code:

    using { sap.capire.incmgt.Incidents } from '@capire/incidents';
    
    extend Incidents with {
      component   : String  @title: 'Component';
      orientation : String  @title: 'Panel Orientation';
      weather     : String  @title: 'Weather Conditions';
      output      : Decimal @title: 'Panels'' Power Output';
      battery     : Decimal @title: 'Battery Fill Level';
    };
  3. Verify that there are no red underlines in your editor and that no errors are reported. It should look similar to this:

    image-20221114070740524

Enhance Fiori UI

Now that we have added the fields to our domain model, we need to ensure they appear in our UI. To achieve this, we extend the respective Fiori annotations as follows:

  1. Open file app/fiori.cds. The provided template looks like this:

    //
    // Extensions for Fiori UIs
    //
    
    using { IncidentsService } from './extensions';
    
    /** Add your ext fields to list pages */
    annotate IncidentsService.Incidents with @(
      UI.LineItem: [
        ... up to { Value: urgency }, //> new columns go after this one
        { Value: ext_field1 },
        { Value: ext_field2 },
        ... //> rest of pre-defined columns go here
       ],
    );
    
    
    /** Add your ext fields to details pages */
    annotate IncidentsService.Incidents with @(
      UI.Facets: [
        ... up to { ID: 'OverviewFacet' }, //> we want a new facet after this pre-defined one
        { Label: 'System Details', $Type: 'UI.ReferenceFacet', Target: '@UI.FieldGroup#SystemDetails' },
        ... //> rest of pre-defined facets go here
      ],
      UI.FieldGroup#SystemDetails: {
        Data: [
          { Value : ext_field1 },
          { Value : ext_field2 },
        ]
      }
    );
  2. Add the component field to the Incidents list page by replacing the two placeholders (ext_field1 and ext_field2) lines within the UI.LineItem section like that:

    // Add your ext fields to list pages
    annotate IncidentsService.Incidents with @(
      UI.LineItem: [
        ... up to { Value: urgency }, //> new columns go after this one
        { Value: component }, //> our new column 
        ... //> rest of pre-defined columns go here
      ],
    );
  3. Add all new fields in a new section System Details to the Incidents details pages by replacing the contents of the Data array within the UI.FieldGroup #SystemDetails section as follows:

    // Add your ext fields to details pages
    annotate IncidentsService.Incidents with @(
      UI.Facets: [
        ... up to { ID: 'OverviewFacet' }, { 
          Label: 'System Details', $Type: 'UI.ReferenceFacet', 
          Target: '@UI.FieldGroup#SystemDetails' 
        }, 
        ... //> rest of pre-defined facets go here
      ],
      UI.FieldGroup#SystemDetails: { // our new section
        Data: [
          { Value : component },
          { Value : orientation },
          { Value : output },
          { Value : battery },
          { Value : weather },
        ]
      }
    );

Test-Drive Locally

Start the server by running the following command in the Terminal:

cds watch --port 4006

The server will start and the output will be displayed in the Terminal, as shown below:

image-20221114080542194

👉 Note: In this hands-on, we run the server on port 4006 to avoid conflicts with the actual incidents app running in parallel on ports 4004, 4005. In a real extension project, you would be independent of the base project and would not require this step.

Test-Explore the UI

  1. Open the Fiori UI by clicking on the [ Open in New Tab ] button in the toaster showing up (or Cmd/Ctrl-click on the server URL in the trace output), as explained in exercise 1. In response to that, you should now see CAP's default index page, as shown below:

image-20221114081441324

  1. Click on the first Fiori preview link highlighted above. This will take you to the Incidents list, which now includes the newly added column Component as in the screenshot below:

    image-20221114082351422

  2. Next click on the first row to view the details page for that incident. You will see the new section System Details with all the added columns, as shown below:

    image-20221114082641913

  3. Feel free to [ Edit ] the data, enter some values, [ Save ] your changes, and observe the updated information displayed on the details page. You can navigate back to the list page and verify that the entered values appear in the Component column of the first row.

Note: Since we are working on an extension project only, the extended application will start as an incomplete look-alike of the real incidents app without any custom logic active. However, the provider of the incidents application has included some test data with the extension project template, so you will immediately see some data in the UI. The test data is available as .csv files in the ./test/data folder.

Push to Test Tenant

After performing the initial check, we are now ready to test the extension in the real environment using our test tenant. Follow the steps below:

  1. Switch back to the t1x project and open a new Terminal. Make sure you select the bash Terminal, not the one running the server. Create a new Terminal if there is no such option besides the server Terminal.

    bash

  2. Build the extension using the following command:

    cds build

The log output will indicate that the following files have been generated:

```sh
[cds] - done > wrote output to:
   gen/ext/extension.csn
   gen/ext/package.json
   gen/extension.tgz
```

The first two are the generated model in .csn format and your package.json. The last file is a packed archive containing everything.

  1. Activate the extension by pushing the archive to the server using the following command:

    cds push --to http://localhost:4005 -u bob

Note: This will deploy to the test tenant t1-ext since bob is assigned to that tenant.

  1. When prompted for password, simply just press Enter without any input. You should see an output similar to the following:

    image-20221114100118531

Upon receiving the pushed extension, the CAP runtime will automatically perform the following actions:

* Validate the extension
* Dynamically extend the loaded model for the `t1-ext` tenant
* Re-deploy the extended model to the database, adding the new fields
* Refresh all bootstrapped services to serve the extended model
* Store the extension definitions in a system table 
* Re-apply the extensions after each upgrade of the base application
  1. Test the UI again, but this time with the real deployed app for test tenant t1-ext:

    • Switch back to the LaunchPad for AD264 browser tab:

      image-20221110164840680

    • Click on button [ Bob (t1-ex) ]

    • Incidents list page should appear, including the newly added Components column.

    • Continue testing the UI as you did before with the server running locally. Follow the instructions in the previous section.

Push to Prod (Activate)

Once all the previous steps have been successfully executed and the necessary approvals from responsive administrators have been obtained, we can finally activate the extension for all users of the t1 customer/tenant. To do so, we need a user with admin or cds.ExtensionDeveloper privileges for the production tenant, which in our setup is alice.

  1. Switch back to the browser tab of our t1x project

  2. In the Terminal run cds push command again, this time suing the alice user instead of bob:

    cds push --to http://localhost:4005 -u alice

Note: This will deploy to production tenant t1 since alice is assigned that tenant.

  1. Finally, test the UI again as you did before with the server running 'locally', but this time with the real incidents application instead of the mock version, and using the production tenant.

    • Go back to the Launchpad for AD264 browser tab

      image-20221110164840680

    • Click the [ Alice (t1) ] button .

    • The Incidents list page should appear, including the newly added Components column.

    • Continue testing the UI as you did before with the server running locally. Follow the instructions in the previous section.

Summary

In Bob's shoes, we have successfully added solar panel system-specific extension fields to the incidents objects, customizing the base incidents management SaaS application to meet the specific needs of customer 1.

CAP Spotlights:

  • CDS as Ubiquitous Modelling Language — already in exercise 1 when we built the base application, and also in this exercise, when we added extensions, all we do is captured in CDS. Domain modelling, service definitions, UI definitions via Fiori annotations, adding extension fields and extending UIs correspondingly.
  • CDS Aspects for Extensibility — all CDS definitions are intrinsically extensible, using the keywords extend or annotate we can add new fields, new relationships (associations and compositions), new annotations or override annotations. And as everything is expressed in CDS, we can apply this same technique to extend everything.

In the next exercise 4 — Pre-built Extensions we will show how to share and reuse such extensions, for example as a partner providing pre-built industry-specific verticalizations to given SaaS applications.