Skip to content

3. Custom Extensions

Daniel edited this page Jan 16, 2023 · 32 revisions

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

Now we change roles and slip into the shoes of a SaaS customer, more specifically an extension developer named bob, who got the job of extending the subscribed Incidents Management app to tailor it to the specific needs of customer t1.

Customer t1 happens to be a vendor of Solar Panels, sold to private home owners. When creating incidents, the extension should request and allow to enter additional information, such as affected component (e.g. panels, batteries, controller unit, ...), panel orientations, weather conditions, etc.

The following sections are an excerpt of the SaaS Extensibility guide in capire.

Add Test Tenant for Extension

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

  1. Set up a test tenant t1-ext by entering this in our Terminal:

    cds subscribe t1-ext --to http://localhost:4004 -u alice
  2. Assign extension developers for the test tenant. As we’re using mocked auth, mocking this step by simply adding the following to the base app’s package.json, assigning bob as extension developer for t1-ext:

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

    Copy and insert the respective lines at the right place in the package.json.

Start Extension Project

SaaS providers usually provide project-specific guides and project templates for extensions. So did we before for the incidents management sample and the project template is enclosed with the mono repo at ./x-t1. So for our exercise we simply open that project in a new BAS window:

  1. Open a new BAS window: Menu > File > New Window (→ details as in exercise 1)

  2. Open the cloned sub project teched2022-AD264/x-t1 (→ also as in exercise 1)

  3. As a result of this your browser window should look like that:

    image-20221114063037645

Essentialy an extension project is a minimalistic CAP project basically just containing a package.json file, which in our case looks like that:

image-20221114064753498

It basically configures the extension's package name, which can be chosen freely, and the extends configuration which specifies the namespace of the base application's CDS models, which we'll learn more about in the next section.

Our template has som additional files, prepared by the provider of the SaaS application, which serve as starting points for actual extensions, as we'll also learn more about below.

Pull Latest Base Model

To get tools support when creating extensions, such as code completion, error highlighting, as well as the ability to compile extension definitions, we need the base application's latest CDS model. Get that as follows...

  1. Open a Terminal

  2. Copy & paste and run this command:

    cds pull --from http://localhost:4004 -u bob

    This will pull the model from test tenant t1-ext automatically as bob is assigned to it.

  3. When prompted for password just 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 can actually specify the extension, that is adding additional fields to Incidents to capture specific properties of solar panel installations. Do so as follows...

  1. Open file app/extensions.cds → the provided template looks like that:

    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 so the outcome looks like that:

    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. If everything is fine there shouldn't be any red underlines in your editor and no errors reported:

    image-20221114070740524

Enhance Fiori UI

Having the fields added to our domain model, we also want them to show up in our UI, of course. To do so, we extend the respective Fiori annotations as follows...

  1. Open file app/fiori.cds → the provided template looks like that:

    //
    // 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 placeholder (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 Incidents details pages by replacing the contents of the Data array within the UI.FieldGroup #SystemDetails section like that:

    /** 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

Having added extension fields and enhanced our UIs accordingly, we can now have a very initial test-drive to check whether everything is as expected. We can use the pre-configured Launch Configuration enclosed with the extension template project to simply run the server as in exercise 1:

  1. Click on the Run and Debug icon in the Activity Bar:
  2. Click on the Start Debugging button to the left of cds run
  3. In response to that the server is started with output shown in the Terminal like that: image-20221114080542194

Alternatively you can start the server with this command from the Terminal:

cds watch --port 4005

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

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), again as in exercise 1. → In response to that you should now see CAP's default index page as below:

image-20221114081441324

  1. Click on the first Fiori preview link highlighted above → the Incidents list page should show up including the newly added column Component as in the screenshot below:

    image-20221114082351422

  2. Next click on the first row and the Incidents details page should show up including the new section System Details with all our added columns:

    image-20221114082641913

  3. Go ahead, and [ Edit ] that data, enter some values, [ Save ] your changes, see them displayed in the details page, go back to the list and see your entered value in Component column of the first row.

Note: As we work in an extension project only. the extended application will start as a incomplete look-alike of the real incidents app without any custom logic active. Yet the provider of the incidents application has at least enclosed some test data with the extension project template, so we immediately see something. You find that test data as .csv files in folder ./test/data.

Push to Test Tenant

With that initial check we are ready for the test with the real up, using our test tenant. To do so switch back to the x-t1 browser tab, and run the following commands in the Terminal.

  1. Make sure you selected the bash Terminal, not the one running the server, labeled cds run. Create a new Terminal if there is no such besides the server one.

    bash

  2. Build the extension (this is also a last test before activation):

    cds build

    As the log output indicates this generates the following files:

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

    The first two are the generated model in .csnformat and your package.json. The last one is an packed archive containing everything.

  3. Activate the extension, by pushing the archive to the server:

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

    Note: this will deploy to test tenant as bob is assigned to tenant t1-ext

  4. When promted for password just press Enter without any input. → You should be rewarded with an output like that:

    image-20221114100118531

    Upon receiving the pushed extension the CAP runtime will automatically do the following:

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

Push to Prod (Activate)

After all steps above have been executed successfully, and additional approvals by responsive administrators have been given, we can finally activate the extension for all users of customer/tenant t1. To do so we need a user who has the admin or cds.ExtensionDeveloper privileges for the productive tenant, which in our setup is alice.

  1. Switch back to the browser tab of our x-t1 project

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

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

    Note: this will deploy to prod tenant as alice is assigned to tenant t1

  3. Finally, test the UI again as you did before with the server running 'locally', this time with the real incidents application executing it, not just the mock version, and with productive tenant.

Summary

So, slipped into Bob's shoes we've now added solar panel system-specific extension fields to incidents objects, thereby tailoring the base incidents management SaaS application to the needs of customer 1 — so accomplishing customization.

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.